Appending character to a string in C with strcat - c++

Hi guys I'm still really confused with pointers and I'm wondering if there's anyways to do the following without having to use sprintf:
char a[100], b[100], c[2];
//Some code that puts a string into a
for(i = 0; i<strlen(a); i++)
{
if(a[i] == 'C')
strcat(b, "b");
else if(a[i] == 'O')
strcat(b, "a");
else if(a[i] == 'D')
strcat(b, "1");
else
{
sprintf(c, "%s", a[i]);
strcat(b, c);
}
}
pretty much a for loop looping through a string(an array) and filling up another string with a character(or string) depending on what the character is, if the character ain'T C, O or D it just adds it to the other string.
I can't seem to just do strcat(b, a[i]); and I understand that it wouldn't work because it would try strcat(char *, char) instead of char*, const char*).
Is there anyway I can turn it into a pointer? they still confuse me so much..and I'm new to programming in general just to low level languages...
also what would be the best way to initialize char[]s? that are gonna be filled with a string, what I use right now is :
char ie[30] = ""
Also let me know if there's any easier way to do what
I want and sorry if it's unclear this is obviously a throwaway script but the same concept is used in my script.
Thank you in advance stackoverflow :X

(1) One bug may be in your code:
You are commenting that Some code that puts a string into a, and I think you don't assign any string to b. so by default char b[100]; have garbage value (may not present \0 in b). but string concatenation function expects that b must be a string. So
strcat(b, "b"); <--will Undefined Behavior
(2) A technique to initialize empty string:
Yes you should always initialize you variable (array) with default values like:
char a[100] = {0}, b[100] = {0}, c[2] = {0};
note: remaining elements of a half initialize array would be 0 (null), Initialize a variable assume to be good practice
(3) Yes strcat(b, a[i]); is wrong:
To concatenate string from a[i] on words into b you can do like:
strcat(b, a + i);
yes you are correct strcat(b, a[i]); is not valid indeed.
note: a[i] and (a + i) are not same, a[i] is char type, where as (a + i) is string that is type of a.
Suppose you have following string array a and value of i is 2 then:
+----+----+----+---+---+----+----+----+---+
| 'u'| 's' |'e'|'r'|'5'| '6'| '7'|'8' | 0 |
+----+----+----+---+---+----+----+----+---+
201 202 203 204 205 206 207 208 209 210 211
^ ^
| |
a (a+i)
So in above diagram a values is 201 and type is char [100] (assuming array is 100 in size) (a + i) also points a string from 'e' at address 203. where as a[i] = 'e'
So you can't do strcat(b, a[i]); but strcat(b, a + i); is valid syntax.
Additionally, From #BenVoigt to concat n chars from a from ith position you can do like:
strncat(b, a+i, n);
its will append n char from a+i to b.

Since you want to take a substring of a exactly one character long:
strncat(b, a+i, 1);

Initialize all char array to null so that no garbage values exists in code.
You are appending to garbaged char array.
char a[100]={0}, b[100]={0}, c[2]={0};
Now strcat() function behaves properly.

You seem to be confused in regards to strings. A string isn't just an array. Which book are you reading?
When you first call strcat to operate on b, b isn't guaranteed to be a string. The result is undefined behaviour. This code might seem to function correctly on your system, but if it does then that is by coincidence. I have seen code like this fail in strange ways on other systems. Fix it like this:
char a[100], b[100];
//Some code that puts a string into a
a[x] = '\0'; // <--- Null terminator is required for a to contain a "string".
// Otherwise, you can't pass a to strlen.
for(i = 0; i<strlen(a); i++)
{
if(a[i] == 'C')
b[i] = 'b';
else if(a[i] == 'O')
b[i] = 'a';
else if(a[i] == 'D')
b[i] = '1';
else
b[i] = a[i];
}
b[i] = '\0'; // If you don't put a null character at the end, it isn't a string.
Now, what is a string?

There are many possible ways to do as you wish. There are ways that avoid using strcat() and sprintf() altogether — see below; you can avoid sprintf() while continuing to use strcat().
The way I'd probably do it would keep a record of where the next character is to be added to the target string, b. This will be more efficient since repeatedly using strcat() involves quadratic behaviour as you build up a string one character at a time. Also, it is generally best to avoid using strlen() in the loop condition for the same reason; it is (probably) evaluated on each iteration, so that it too leads to quadratic behaviour.
char a[100], b[100];
char *bp = b;
//Some code that puts a string into a
size_t len = strlen(a);
for (int i = 0; i < len; i++, bp++)
{
if (a[i] == 'C')
*bp = 'b';
else if(a[i] == 'O')
*bp = 'a';
else if(a[i] == 'D')
*bp = '1';
else
*bp = a[i];
}
*bp = '\0'; // Null-terminate the string
You could also do without the pointer by using the index variable i to assign to b (as long as you only add one character to the output for each input character):
char a[100], b[100];
//Some code that puts a string into a
size_t len = strlen(a);
for (int i = 0; i < len; i++)
{
if (a[i] == 'C')
b[i] = 'b';
else if(a[i] == 'O')
b[i] = 'a';
else if(a[i] == 'D')
b[i] = '1';
else
b[i] = a[i];
}
b[i] = '\0'; // Null-terminate the string
As long as the string in a is short enough to fit, the code shown (either version) cannot overflow b. If you sometimes added several characters to b, you'd either need to indexes (i and j), or you could increment the pointer bp in the first version more than once per loop, and you'd need to ensure that you don't overflow the bounds of b.

Related

C++ Check and modify strings / String subscript out of range

I'm trying to make a program which modifies words in a specific manner:
It should first check the ending of the words and then proceed to modify them. I won't explain it in detail, because it doesn't make much sense in English.
I've written the following:
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Por favor, introduzca los gentilicios separados por la tecla enter, para finalizar, escriba OK" << '\n';
string name[10];
string place[10];
for (int i(0); (i < 10); i++)
{
getline(cin, name[i]);
if (name[i] == "OK") //Error here
break;
}
for (int i(0); (i < 10); i++)
{
place[i] = name[i];
if (name[i][name[i].length() - 1] == 'c')
{
if (name[i][name[i].length()] == 'a' || (name[i][name[i].length()] == 'o') || (name[i][name[i].length()] == 'u'))
place[i][place[i].length() - 1] = 'q';
place[i][place[i].length()] = 'u';
place[i] = place[i] + "istan";
}
else if (name[i][name[i].length()] == 'a' || name[i][name[i].length()] == 'e' || name[i][name[i].length()] == 'i' || name[i][name[i].length()] == 'o' || name[i][name[i].length()] == 'u')
{
place[i][place[i].length()] = 'i';
place[i] = place[i] + "stan";
}
if (name[i][name[i].length()] == 's')
place[i] = place[i] + "tan";
else {
place[i] = place[i] + "istan";
}
place[i][0] = toupper(place[i][0]);
}
for (int i(0); (i < 10); i++)
{
cout << place[i] << '\n';
}
return 0;
}
Now I'm getting the error "String subscript out of range" . I would like to know where is the error exactly. I know it prompts when I write "OK", at line "18".
The condition i <= sizeof(name). sizeof(name) returns the size of the array in bytes, not the number of elements in it. Even if it returned the number of elements, <= is wrong and would cause an out-of-bounds access (should be <).
To loop through all elements in an array, you can use the range-based for-loop:
for(auto& n : name)
{
getline(cin, n);
if (n == "OK")
break;
}
Or to do it the right way with the C-style for-loop:
for (int i(0); i < sizeof(name)/sizeof(name[0]; i++)
{
…
}
Here:
for (int i(0); (i <= sizeof(name)); i++)
sizeof(name) is the size in bytes of the array, which as it is an array of std::string is effectively meaningless. If you want to iterate over 10 items, simply say so (note also that less-than-or-equals is also wrong here):
for (int i = 0; i < 10; i++)
And here:
getline(cin, name[i]);
whenever you perform input you must check the return value of the input function and handle any errors:
if( ! getline(cin, name[i]) ) {
// handle error somehow
}
And here:
string * p;
you do not want to be dealing with pointers to strings. If you want to access the contents of a string, you use operator[] or other string member functions on the string.
std::strings are not like cstrings. You can just grab a part of them using a std::string*. When you do
*(p+ (name[i].length()-2))
You actually say advance the address stored in p by name[i].length()-2 amount and access that string. If you go past the end of the name array then that is undefined behavior. If not you still haver a std::string which cannot be compared with a char. If you want to check if the string ends with "ca" then you can just use
if (name[i].substr(name[i].size() - 2) == "ca")
You're last loop is doing something quite funky. There's no need to go that far. You can just do something like:
if (name[i][name[i].length - 2] == 'c')
To compare the next to last character with c. And a very similar test to compare the last one with a.
To clarify why what you're doing is not OK, you first get p as a pointer to a string to the current element. Then you do some pointer arithmetic p + (name[i].length - 2), which still results in a pointer to a string. Finally, you dereference this, resulting in a string. Which you can't compare to a char. Moreover, the pointer was to some arbitrary address in memory, so the dereference would produce a string with very bad data in it. Quite arbitrary, one might say. If you tried to work with it you'd break your program
You seem to be working with the string as one would with a C-like string, a char*. The two are not the same, even though they represent the same concepts. A C++ string, usually, has a size field, and a char* pointer inside it, as well as a bunch of other logic to make working with it a char-m.
Because you aren't comparing against a specific char in the string, you're comparing against a string.
Considering the following bit of code:
*(p + (name[i].length() - 2))
This evaluates to a string because you are taking p (a string*) and concatenating a char to it. This means it's still a string (even though it's a one-character string), thus the other side of the equation won't be comparable to it.
What you need here instead is this:
if (name[i][name[i].length() - 2] == 'c')
Since name[i] is already a string, we can just get the char from it using the code above. This does return char, so it's comparable. This also allows you to get rid of the whole string* bit as it is not needed.
First, (i <= sizeof(name)) is wrong, it should be i < sizeof(name) / sizeof(*name). sizeof(array) return the size of array in bytes, you need to divide the size of an array's element to actually get the maximum element count of an array. If you find that complicated then use std::vector:
vector<string> name(10); //a vector of size 10
for (size_t i = 0; i < name.size(); i++) //name.size(), simple
Secondly, you need to keep track of how many strings in your name array. Or you need to check if name[i] == "OK" then break the second loop (similar to the first loop). name[i] after "OK" are invalid.
Thirdly, don't use *(p+ (name[i].length()-2)). If you want the second last character of name[i], you can write it as name[i][name[i].size()-2] or name[i].end()[-2] or end(name[i])[-2]
If you want to check if the word ends in "ca", then you can use substr:
if (name[i].substr(name[i].size() - 2) == "ca")
{
//...
}

C++ for loop terminated automatically safely for char * when index cross its length

Consider the following for loop.
char temp[100];
char *str = "abab";
int i, j;
for (i = 0, j = 0; temp[i] = str[j];j++)
{
if (str[j] == temp[i])
i++;
else
i--;
}
It does not have terminating condition but still it manages to terminates after traversing "str".
Please explain.
The assignment expression temp[i] = str[j] has a value, which is the value that was assigned.
Since str is \0 terminated (character value 0), when the loop reaches it and assigns it to temp, the expression evaluates to 0. And the loop condition becomes false.
The loop does have a terminating condition, namely
temp[i] = str[j]
As the value of that is str[j], the loop condition boils down to
for (j = 0; str[j]; j++)
This certainly traverses str exactly once since str[j] evaluates to false in this context if and only if str[j] == '\0'. '\0' is a special character that terminates all string literals and is used to terminate C style strings.
In the "real world", i.e. outside of toy programs, you should avoid "clever" code like this, even if it works.
Also note that the conversion from string literals to char* is deprecated and that every attempt to modify a string literal invokes undefined behavior. Use
const char *str = "abab";
instead.
The condition is temp[i] = str[j]. That assigns and returns the assigned value. Therefore it will be evaluated to true for everything different to \0 and false, when the terminating \0 has been reached.
#include <iostream>
using namespace std;
int main()
{
char temp[100];
const char *str = "abab";
int i, j;
for (i = 0, j = 0; temp[i] = str[j]; j++)
{
if (str[j] == temp[i])
i++;
else
i--;
}
cout << (temp[4] ? "true" : "false") << endl;
}
Printing shows that the condition of the last iteration evaluates to false because it's zero ('\0' as end of string).
from the looks of it, the loop run it course as its scan the whole word "abab" and the condition for the loop to end is str = temp, the final output of those variables are 4 = 4

Having an issue with character displacemet

I'm reading a file with a line of text. I'm reading the file and changing the characters based on a displacement given by the user. While it works for some characters, it doesn't for others beyond a certain point.
My file contains this text: "This is crazy".
When I run my code with a displacement of 20, this is what I get:
▒bc▒ c▒ w▒u▒▒
string Security::EncWordUsingRot(int rotNum, string word)
{
rotNum = rotNum%26;
string encWord = word;
for (int i = 0; i < word.size(); i++)
{
char c = word[i];
c = tolower(c);
if ((c < 'a') || (c > 'z'))
encWord[i] = c;
else
{
c = (c + rotNum);
if (c > 'z')
c = (c - 26);
}
encWord[i] = c;
}
return encWord;
}
*EDIT**
I changed the commented sections to correct my error. I changed unsigned char c = word[i] back to char c = word[i]. I also added another two lines of code that took care of the value of c being lower than 'a'. I did this because I noticed an issue when I wanted to essentially return the encrypted statement to its original form.
string Security::EncWordUsingRot(int rotNum, string word)
{
rotNum = rotNum%26;
string encWord = word;
for (int i = 0; i < word.size(); i++)
{
char c = word[i]; //removed unsigned
c = tolower(c);
if ((c < 'a') || (c > 'z'))
encWord[i] = c;
else
{
c = (c + rotNum);
if (c > 'z')
c = (c - 26);
if (c < 'a') //but I added this if statement if the value of c is less than 'a'
c = (c + 26);
}
encWord[i] = c;
}
return encWord;
}
Change:
char c = word[i];
To:
unsigned char c = word[i];
In C and C++ you should always pay attention to numeric overflow because the language assumption is that a programmer will never make such a mistake.
A char is a kind of integer and is quite often 8 bits and signed, giving it an acceptable range of -128...127. This means that when you store a value in a char variable you should never exceed those bounds.
char is also a "storage type" meaning that computations are never done using chars and for example
char a = 'z'; // numeric value is 122 in ASCII
int x = a + 20; // 122 + 20 = 142
x will actually get the value 142 because the computation did not "overflow" (all char values are first converted to integers in an expression)
However storing a value bigger that the allowable range in a variable is undefined behaviour and code like
char a = 'z'; // numeric value is 122 in ASCII
char x = a + 20; // 122 + 20 = 142 (too big, won't fit)
is not acceptable: the computation is fine but the result doesn't fit into x.
Storing a value outside the valid range for signed chars in a signed char variable is exactly what your code did and that's the reason for the strange observed behaviour.
A simple solution is to use an integer to store the intermediate results instead of a char.
A few more notes:
A few functions about chars are indeed handling integers because they must be able to handle the special value EOF in addition to all valid chars. For example fgetc returns an int and isspace accepts an int (they return/accept either the code of the char converted to unsigned or EOF).
char could be signed or not depending on the compiler/options; if unsigned and 8-bit wide the allowable range is 0...255
Most often when storing a value outside bounds into a variable you simply get a "wrapping" behavior, but this is not guaranteed and doesn't always happen. For example a compiler is allowed to optimize (char(x + 20) < char(y + 20)) to (x < y) because the assumption is that the programmer will never ever overflow with signed numeric values.

Input C-style string and get the length

The string input format is like this
str1 str2
I DONT know the no. of characters to be inputted beforehand so need to store 2 strings and get their length.
Using the C-style strings ,tried to made use of the scanf library function but was actually unsuccessful in getting the length.This is what I have:
// M W are arrays of char with size 25000
while (T--)
{
memset(M,'0',25000);memset(W,'0',25000);
scanf("%s",M);
scanf("%s",W);
i = 0;m = 0;w = 0;
while (M[i] != '0')
{
++m; ++i; // incrementing till array reaches '0'
}
i = 0;
while (W[i] != '0')
{
++w; ++i;
}
cout << m << w;
}
Not efficient mainly because of the memset calls.
Note:
I'd be better off using std::string but then because of 25000 length input and memory constraints of cin I switched to this.If there is an efficient way to get a string then it'd be good
Aside from the answers already given, I think your code is slightly wrong:
memset(M,'0',25000);memset(W,'0',25000);
Do you really mean to fill the string with the character zero (value 48 or 0x30 [assuming ASCII before some pedant downvotes my answer and points out that there are other encodings]), or with a NUL (character of the value zero). The latter is 0, not '0'
scanf("%s",M);
scanf("%s",W);
i = 0;m = 0;w = 0;
while (M[i] != '0')
{
++m; ++i; // incrementing till array reaches '0'
}
If you are looking for the end of the string, you should be using 0, not '0' (as per above).
Of course, scanf will put a 0 a the end of the string for you, so there's no need to fill the whole string with 0 [or '0'].
And strlen is an existing function that will give the length of a C style string, and will most likely have a more clever algorithm than just checking each character and increment two variables, making it faster [for long strings at least].
You do not need memset when using scanf, scanf adds the terminating '\0' to string.
Also, strlen is more simple way to determine string's length:
scanf("%s %s", M, W); // provided that M and W contain enough space to store the string
m = strlen(M); // don't forget #include <string.h>
w = strlen(W);
C-style strlen without memset may looks like this:
#include <iostream>
using namespace std;
unsigned strlen(const char *str) {
const char *p = str;
unsigned len = 0;
while (*p != '\0') {
len++;
*p++;
}
return len;
}
int main() {
cout << strlen("C-style string");
return 0;
}
It's return 14.

"The Debugger has exited due to signal 10" when writing a char* iteration

So I have a program that makes char* stuff lowercase. It does it by iterating through and manipulating the ascii. Now I know there's probably some library for this in c++, but that's not the point - I'm a student trying to get a grasp on char*s and stuff :).
Here's my code:
#include <iostream>
using namespace std;
char* tolower(char* src);
int main (int argc, char * const argv[])
{
char* hello = "Hello, World!\n";
cout << tolower(hello);
return 0;
}
char* tolower(char* src)
{
int ascii;
for (int n = 0; n <= strlen(src); n++)
{
ascii = int(src[n]);
if (ascii >= 65 && ascii <= 90)
{
src[n] = char(ascii+32);
}
}
return src;
}
( this is not for an assignment ;) )
It builds fine, but when I run it it I get a "The Debugger has exited due to signal 10" and Xcode points me to the line: "src[n] = char(ascii+32);"
Thanks!
Mark
Yowsers!
Your "Hello World!" string is what is called a string literal, this means its memory is part of the program and cannot be written to.
You are performing what is called an "in-place" transform, e.g. instead of writing out the lowercase version to a new buffer you are writing to the original destination. Because the destination is a literal and cannot be written to you are getting a crash.
Try this;
char hello[32];
strcpy(hello, "Hello, World!\n");
Also in your for loop, you should use <, not <=. strlen returns the length of a string minus its null terminator, and array indices are zero-based.
As Andrew noted "Hello World\n" in code is a read-only literal. You can either use strcpy to make a modifiable copy, or else try this:
char hello[] = "Hello, World!\n";
This automatically allocates an array on the stack big enough to hold a copy of the literal string and a trailing '\0', and copies the literal into the array.
Also, you can just leave ascii as a char, and use character literals instead of having to know what the numeric value of 'A' is:
char ascii;
for (int n = 0; n < strlen(src); n++)
{
ascii = src[n];
if (ascii >= 'A' && ascii <= 'Z')
{
src[n] = ascii - 'A' + 'a';
}
}
While you're at it, why bother with ascii at all, just use src[n]:
for (int n = 0; n < strlen(src); n++)
{
if (src[n] >= 'A' && src[n] <= 'Z')
{
src[n] -= 'A' - 'a';
}
}
And then, you can take advantage of the fact that in order to determine the length of a c-string, you have to iterate though it anyway, and just combine both together:
for (char *n = src; *n != 0; n++)
if (*n >= 'A' && *n <= 'Z')
*n -= 'A' - 'a';