I've been writing a C study program on encryption and met an unexpected problem:
When I include parameters into main() function (as int main(int argc, char* argv[])) and try to declare a separate array of chars in my code (char array[2] = {'a','b'};), which has nothing to do with main() arguments, that array somehow gets length of 8 instead of 2 and some junk is added to it when I try to print it out (e.g. ab ?iQ?).
Whereas if I declare main() without any parameters (int main()), the declared char array behaves normally: gets size of 2 and prints out like ab.
Do parameters in main() function set limits on minimum size of arrays in code?
Using strlen() on a char array without a nul termination byte causes undefined behavior.
A string in c, consists of a sequence of non-nul bytes terminated by a nul -> '\0' byte, so this
char array[2] = {'a','b'};
should be
char array[3] = {'a', 'b', '\0'};
for strlen() to work properly.
You can implement a strlen() like this
size_t stringlength(const char *string)
{
size_t length;
length = 0;
while (string[length] != '\0')
length++;
return length;
}
so if the '\0' is not there, it will keep reading from the array beond it's bounds cause the undefined behavior mentioned above.
Related
char * convert(char * s, int numRows){
char rows[numRows][strlen(s)];
memset( rows, '\0', numRows*strlen(s)*sizeof(char) );
int curRow=0;
bool goingDown=false;
int len=0;
char *str=s;
for(char c=*str;c=*str;++str){
len=0;
while(rows[curRow][len]){len++;}
rows[curRow][len]=c;
if(curRow==numRows-1||curRow==0){goingDown=!goingDown;}
if(goingDown){curRow++;}
else{curRow--;}
}
char *zig=malloc(strlen(s)+1);
*zig='\0';
int i=0;
for(char *row;i<numRows;i++){
row=*(rows+i);
zig=strcat(zig,row);
}
return zig;
}`
I am trying to implement the solution in c. Leetcode has thrown this to me:
==26==ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address 0x7ffd37cc1741.
But I have no idea what is the problem. All I could guess is that row variable has something wrong. Test case being "A" 1 .
row: 0x7ffd37cc1740 "A\vI\037\244U"
So how could I fix this code? Trying to learn more about c.
Problem solved now. I shall declare 2d char rows with strlen(s)+1. Thus the row variable would be nul terminated, which would be valid for strcat argument.Thanks!
Your problem (or at least, one of) is in the first line of the function definition.
char rows[numRows][strlen(s)];
declares a 2D char array on the stack. You should not declare variables with size dependent on runtime in the stack (in this case, because of the variables s and numrows passed to the function).
You should instead allocate memory dynamically using malloc or alternatives -
char ** rows = malloc(sizeof(char *)*numrows); // allocate memory to store `num_rows` char pointers
// for each element in rows, create a pointer with num_columns of chars amount of space
for(int i = 0 ; i < num_columns ; i++){
rows[i] = malloc(sizeof(char) * num_columns); // store pointer in rows[i]
// memset this memory as needed
}
// now rows[x][y] will be one char
Note that you will need to allocate one more char than the number of characters in your string to add the null terminator in C char arrays.
This will likely get rid of the buffer overflow error, although your program may contain other flaws.
I am a beginner of c. Today when i write a c program, I find some strange thing.
What i want it to show is abc, but it show abcefg. I want to know why it's so shown.
the code is:
#include <stdio.h>
int main() {
char a[3] = "abc";
char b[3] = "efg";
printf("%s", a);
return 0;
}
It's answer is not abc but abcefg
Strings in C are zero terminated with '\0'. "abc" actually is { 'a', 'b', 'c', '\0' }, which is 4 chars. Your array a only has room for 3 chars so the '\0' isn't stored. When printf() tries to print the string stored in a it reads and prints one character a time until it encounters a terminating '\0', but there is none. So it continues reading and printing. And it happens that b is right next to a in memory, so the content of b gets printet as well.
Cure:
#include <stdio.h>
int main(void)
{
char a[4] = "abc";
char b[4] = "efg";
printf("%s", a);
}
or, even better, don't specify a size for the arrays at all. Let the compiler figure out the correct size based on the initializer "abc":
#include <stdio.h>
int main(void)
{
char a[] = "abc";
char b[] = "efg";
printf("%s", a);
}
char a[3] = "abc"; misses space for the 0-terminator, so printf will read out of bounds (undefined behavior) into the next memory location , where it finds the b array (by luck).
You should use char a[4] = "abc"; or char a[] = "abc";.
When you do not write an array size, the compiler will evaluate the minimum size from the initialization.
char b[3] = "efg"; has the same problem, but it seems that you are lucky enough to have a 0 byte afterwards.
Here's my code
#include <stdio.h>
#include <string.h>
int main(){
char pal[8] = "ciaooaic";
char pal1[7] = "ciaoaic";
int lenPal = strlen(pal);
int lenPal1 = strlen(pal1);
printf("strlen('%s'): %d\n", pal, lenPal);
printf("strlen('%s'): %d\n", pal1, lenPal1);
return 0;
}
The problem is that when I run this code the output is:
strlen('ciaooaicP#'): 11
strlen('ciaoaic'): 7
The first string has also another non-printable char between P and #. I'm a noob, so maybe I missed something obvious. Can someone help me?
edit:
just give one extra space like char pal[9] = "ciaooaic"; char pal1[8] = "ciaoaic";
It works, but why? I understand that there should be a space for \0, but "ciaoaic" works without it...
1. You don't leave room for a null terminator, as you pass them to strlen(), therefore your code exhibits undefined behaviour -
char pal[8] = "ciaooaic";
char pal1[7] = "ciaoaic";
Leave a space for '\0'. Declare and initialize like this -
char pal[9] = "ciaooaic";
char pal1[8] = "ciaoaic";
2. And strlen() returns size_t not int , so write like this -
size_t lenPal = strlen(pal);
size_t lenPal1 = strlen(pal1);
and use %zu specifier to print both these variables.
You have not kept space for the NULL terminating character \0.
Either increase the size of the array by 1
char pal[9] = "ciaooaic";
char pal1[8] = "ciaoaic";
OR
Do not specify the length at all
char pal[] = "ciaooaic";
char pal1[] = "ciaoaic";
Both the above answers are sufficient to solve your doubts.
Increase the the length of both pal and pal1 by one as there there is no space for the assignment of the null character( '\0') at the end.
However there is small trick to print the non null terminated character using printf
printf("strlen('%.*s'): %d\n",8, pal, lenPal);
printf("strlen('%.*s'): %d\n",7, pal1, lenPal1);
Link for the above trick:BRILLIANT
I am writing a simple function in C that should build a char array from string "abc" – so it should build {'a','b','c'} – and return a pointer to that array. Here is my code for this function:
char * makeArr()
{
static char arr[3];
sprintf(arr, "%s\n", "abc");
return arr;
}
Problems occur when I call this method in main:
int main(int argc, char *argv[])
{
char *arr[3];
arr = makeArr();
return 0;
}
The compiler is complaining about casting / conflicting types. I've been playing with pointers, casting and dereferencing for quite a while now, but can't seem to get it to work. Please let me know where my logic is wrong.
Hmm ... there are several errors in this code. Let's start with the most obvious your compiler complains about:
char *arr[3];
This line declares arr to be an array of three pointers to char. What you return from your function is a single pointer to a char -> doesn't match.
Next:
static char arr[3];
sprintf(arr, "%s\n", "abc")
Here you reserve 3 chars. the sprintf() will write 5 chars. %s is replaced by the 3 characters in your string literal "abc". You add a newline character and then a 0 is added as the marker for the end of the "string". Makes 5. This btw is undefined behavior. You write past the end of your array. Code like this can be compiled, but there's no guarantee at all about what will happen at runtime.
Doing a cut here. You should read about arrays and pointers in C. If the text you're reading claims they are the same ... stop right there and find a better text. They aren't.
I'll try to explain this here briefly, so it's suitable for the Q&A style.
An array in C indeed is a contiguous space of several values. char arr[3] means a variable that holds 3 chars.
On the other hand, a char * is just a pointer pointing to a char -- this could be the first element of an array.
In C, you can't pass arrays as function parameters, and you can't return arrays from a function. Trying to do so leads to an implicit conversion: What is actually passed is a pointer to the first element of that array.
I think the last bit of information missing is what a string literal in C is: it's an array (anonymous, e.g., it doesn't have a name) containing all the characters in the double quotes plus a 0 appended. The 0 marks the end of a "string" in C.
In an expression, a string literal evaluates to a pointer to the first element.
So, something like this:
char *foo = "bar";
will lead to foo pointing to the b of the array. It's like writing
static const char no_name_0[] = { 'b', 'a', 'r', 0 };
char *foo = &(no_name_0[0]);
Among other things, you confused:
char arr[3]; // array of 3 chars.
and,
char *arr[3]; // array of 3 pointers to char.
In main(), you should only write char *arr;
Firstly, char arr[3]; is too snall to store "abc\n". It must have at least 5 elements including terminating null-character.
Then, char *arr[3]; is a 3-element array of char*.
You should assign makeArr()'s return value (it has char* type) to arr[0] or another element, or you should change the type of arr in main function to char*, which is the same type as makeArr()'s return value.
Moreover, this makeArr() doesn't make any array and returns (a pointer to) the existing array. Yoy should use malloc() to "make an array".
UPDATE:
Assigning a value of char* to the array char arr[10]; seems invalid in C.
You should use strcpy() or strncpy() (safer than strcpy()) to copy the string stored in the array between arrays.
Pass the array as an argument and modify it in the called function, would be easier. If you're statically creating the array and there's no need to allocate memory, don't, just pass around your pointers to the functions to be modified by reference
void makeArr(char arr[]){
sprintf(arr, "%s\n", "abc");
}
Simply pass the existing declared array to the makeArr function...
int main(int argc, char *argv[]) {
char arr[10];
makeArr(arr);
return 0;
}
You couldn't assign the result of makeArr to arr. I guess that's your casting error. Oversimplifying, arr points to the place on the stack where the array of 10 characters is allocated. So, I'd pass in arr to makeArr as a char*. So, you'd end up with something like this:
#include <stdio.h>
char * makeArr(char *arr)
{
sprintf(arr, "%s\n", "abc");
return arr;
}
int main(int argc, char *argv[])
{
char arr[10];
makeArr(arr);
printf("%s\n", arr);
return 0;
}
I have an array of characters declared as:
char *array[size];
When I perform a
printf("%s", array);
it gives me some garbage characters, why it is so?
http://www.cplusplus.com/reference/clibrary/cstdio/printf/
This url indicates printf takes in the format of: `int printf ( const char * format, ... );
#include <stdio.h>
#include <string.h>
#define size 20
#define buff 100
char line[buff];
int main ()
{
char *array[100];
char *sep = " \t\n";
fgets(line, buff, stdin);
int i;
array[0] = strtok(line, sep);
for (i = 1; i < size; i++) {
array[i] = strtok(NULL, sep);
if (array[i] == NULL)
break;
}
return 0;
}
You declare an array of characters like so:
char foo[size];
You seem to have it mixed up with char *, which is a pointer to a character. You could say
char *bar = foo;
which would make bar point to the contents of foo. (Or, actually, to the first character of foo.)
To then print the contents of the array, you can do one of the following:
// either print directly from foo:
printf("%s", foo);
// or print through bar:
printf("%s", bar);
Note, however, that C performs no initialization of the contents of variables, so unless you specifically set the contents to something, you'll get garbage. In addition, if that garbage doesn't happen to contain a \0; that is, a char with value 0, it will keep on outputting past the end of the array.
Your array is not initialized, and also you have an array of pointers, instead of an array of char's. It should be char* array = (char*)malloc(sizeof(char)*size);, if you want an array of char's. Now you have a pointer to the first element of the array.
Why are we making such a simple thing sound so difficult?
char array[SIZE];
... /* initialize array */
puts(array); /* prints the string/char array and a new line */
/* OR */
printf("%s", array); /* prints the string as is, without a new line */
The char in array after the end of what you want to be your string (ie. if you want your string to read "Hello" that would be the next char after the 'o') must be the terminating NUL character '\0'. If you use a C function to read input that would automatically be appended to the end of your buffer. You would only need to worry about doing it manually if you were individually writing characters to your buffer or something for some reason.
EDIT: As with pmg's comment, the '\0' goes wherever you want the string to end, so if you wanted to shorten your string you could just move it up closer to the front, or to have an empty string you just have array[0] = '\0';. Doing so can also be used to tokenise smaller strings inside a single buffer, just as strtok does. ie. "Part1\0Part2\0Part3\0". But I think this is getting away from the scope of the question.
ie. you wanted to store the first 3 chars of the alphabet as a string (don't know why anyone would do it this way but it's just an example):
char array[4];
array[0] = 'a';
array[1] = 'b';
array[2] = 'c';
array[3] = '\0';
printf("%s\n", array);
If you have something like char array[] = "Hello"; the '\0' is automatically added for you.
char *array[size];
array is not a char * with that, it's more like a char ** (pointer to an array of chars, with is similar to pointer to pointer to char).
If all you need is a C string, either:
char array[size];
and make sure you 0-terminate it properly, or
char *array;
and make sure you properly allocate and free storage for it (and 0-terminate it too).