Dynamic String array dosent work - c

I'm trying to create a dynamic array of 1000 character long strings using calloc:
int i;
char** strarr =(char**)calloc(argc,sizeof(char)*1000);
if(strarr == NULL)
return 0;
strarr[0][0] ='a';
printf("%c\n",strarr[0][0]);
Every time i try to run this code i get segmentation fault on the printf line, i don't get why does this happen (you can assume that argc is bigger than 0)
Thanks
P.s. im sorry that the code is in text format but im using a mobile so i dont have the code feature

Try this:
const int num_of_strings = 255; //argc ?
const int num_of_chars = 1000;
int i;
char** strarr =(char**)malloc(sizeof(char*)*num_of_strings);
if(strarr == NULL)
return 0;
for (i = 0; i < num_of_strings; i++) strarr[i] = (char*)malloc(sizeof(char)*num_of_chars);

Hello and Welcome to the world of undefined behaviour, one of the darkest territories of the C language. Your code has several problems, which cause undefined behaviour in several occasions, but they all get executed, until you reach the printf line, where you are accessing memory, you have not allocated, which is finally caught by your system and, thus, a segmentation fault is produced.
But I think, it would be better to walk ourselves through your code.
The variable i, which is declared in the int i; line is not used anywhere in the code you have posted, but I guess you need it later.
The first piece of code, that is not right, is in this second line, where you declare an array of strings or a char**. That means that you have a pointer to pointers to chars. So, what you really want to do there is allocate memory for those pointers and not for the chars they will point to. Note that a char consumes a different amount of memory than a char*. This line is, thus, the one to go with.
char** strarr = (char**) calloc(argc, sizeof(char*));
This will allocate memory for argc blocks of memory, each of which is of size 4 or 8 bytes, which depends on whether your system is 32 or 64-bit.
You are doing a very good job of checking whether the calloc function returned NULL or not, which is a very good practice overall.
Next, you will want to allocate memory for the strings themselves, that are pointed to by the pointers, for which you allocated memory in the previous line. These lines will do it.
for (int i = 0; i < argc; i++) {
strarr[i] = (char*) calloc(1000, sizeof(char));
}
This will now allocate 1000-character lengthed strings for every element of our argc-sized string array.
After that, you can continue with your code as it is and I think that no errors will be produced. Please accept an additional piece of advice from me. Learn to love valgrind. It is a very helpful program, which you can run your code with, in order to analyse memory. It is my first step, whenever I get a segmentation fault.

Related

Free() a safe-copied string array crashes?

I am trying to run the following C program on an SoC platform (baremetal application).
I have a couple of string arrays, defined as
char *init_array[] = { "foo", "bar bar" }
I know this works as an array of char arrays : *init_array[0] returns the 'f' character, init_array[0] returns the first address of "foo" and &init_array[0] returns the adress where the first address of "foo" is saved. The program I work with is based on that definition, so I am not supposed to change it.
Those arrays are going into a loop, in which there may or may not occur an external alteration. At the following iteration, the array must not carry the 'error'.
One of the solutions I've got was to keep two copies of those arrays, the ones I'm gonna use and ones that will initialize them.
Obviously, this has to be "safe-copied", in order to avoid using the same addresses. I have achieved this, with the following code:
char *my_array[2];
for (int i = 0; i < 2; i++) {
my_array[i] = malloc(strlen(init_array[i]) + 1);
memcpy(my_array[i], init_array[i],
strlen(init_array[i]) + 1);
}
The use of malloc() here (which I would rather avoid, if it's possible) demands the use of free() at the end of every iteration:
for (int i = 0; i < 2; i++) {
// i know it crashes somewhere here
free(my_array[i]);
my_array[i] = NULL; //this is supposed to tackle the issue of the same pointer being freed twice(?)
And it works perfectly, for a small number of iterations. Then, the program crashes, and I have to reset the platform I'm working on. As I mention above, I know it crashes somewhere in the last loop.
I've also read that:
Crashes in malloc(), calloc(), realloc(), or free() are almost always
related to heap corruption, such as overflowing an allocated chunk or
freeing the same pointer twice.
I really can't figure out what I'm doing wrong. Is this a heap corruption issue and how can I handle it or am I wrong from the start?

Segmentation fault while trying to make a dynamically allocated array of strings in C

I am trying to make a function which writes strings and integers to a file. A part of my approach involves creating a 2-D dynamic array to hold a series of strings. There are no compile time errors. However, when I try to run the program (the function plus a main to test it inside) I keep getting a segmentation fault error. I have traced it so far to a particular section of code that I have highlighted below. From what I can tell, it seems that my attempts to open the array are what is causing the seg fault (I have used a debug string with a %p character in it to make sure that the array is actually in the memory). I can't for the life of me tell why though. For convenience, I've only included the code that pertains to the seg fault as well as the declarations/initializations. If you want me to provide the whole main, please let me know. To be clear, I have tried Google for a solution, but everything I've found so far either I don't understand, or doesn't pertain to my situation.
int data_cat_num=0,current_cat=0,col_index=0;
char **cat_names;
printf("How many data categories will there be?:");
scanf("%d",&data_cat_num);
cat_names=malloc(data_cat_num*50);/*Currently defaulting to 50 bytes per name*/
while(current_cat<data_cat_num){
col_index=0;
printf("What is the name of category %d?",current_cat+1);
while(cat_names[current_cat][col_index]!='\n'){/*SEG FAULT TRACED TO THIS*/
cat_names[current_cat][col_index++]=getchar();/*LOOP!!!!!*/
}
cat_names[current_cat][col_index]='\n';
current_cat++;
}
This line:
cat_names=malloc(data_cat_num*50);
doesn't allocate 50 bytes per name; it allocates data_cat_num*50 names. You need to do this:
cat_names = malloc(data_cat_num * sizeof(char *));
int i;
for(i = 0; i < data_cat_num; i++)
cat_names[i] = malloc(50);
The first line allocates data_cat_num names (i.e., char * variables), and later (in the for loop), 50 bytes for each name.
EDIT Also, when you free this memory, you need to do it this way:
for(i = 0; i < data_cat_num; i++)
free(cat_names[i]);
free(cat_names);
This line doesn't do what you think:
cat_names=malloc(data_cat_num*50);/*Currently defaulting to 50 bytes per name*/
cat_names contains pointers to the strings, not the strings themselves. So, here you're allocating enough space to store the pointers to 50 cat names, but not the names themselves.
What you'll need to do is each time you add a name, malloc that as well. So...
cat_names[current_cat] = malloc(maxNameLength*sizeof(char));
char **cat_names;
This says cat_names will be a pointer to a pointer.
cat_names=malloc(data_cat_num*50);/*Currently defaulting to 50 bytes per name*/
This makes cat_names a pointer, but a pointer to garbage because the memory returned by malloc isn't initialized.
cat_names[current_cat][col_index]='\n';
Oops, you used the cat_names contents as a pointer, but it points to garbage.

Why I use pointer's pointer to maintain a two-dimensional array but caught a segmentation fault in C?

I'm new to C (and also programming). Recently I practise a lot in leetcode(oj.leetcode.com) by using C. While I was coding below problem:
http://oj.leetcode.com/problems/reverse-words-in-a-string/
It means if the input is "the sky is blue", the output should be "blue is sky the". I decided to use two-dimensional char array to store the words and print them in reversed order. Here is the code:
void reversed(char s[]) {
int wordcount=0;
char** words;
int i=0;
for(i=0;i<strlen(s);i++)
{
if(!isspace(s[i]))
{
**words=s[i];
*words++;
}
else
{
**words='\0';
wordcount++;
words++;
}
}
for(;wordcount=0;wordcount--,words--)
{
printf("%s", *words);
printf(" ");
}
printf("\n");
But I encountered a segmentation fault, here is the GDB info:
23 for(i=0;i<strlen(s);i++)
(gdb) n
25 if(!isspace(s[i]))
(gdb) n
27 **words=s[i];
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x00401226 in reversewords (s=0x22abe0 "the sky is blue") at reversewords.c:27
27 **words=s[i];
Can anybody can help me to figure out what's going on here and how to correct my code to make it work? Thanks in advance!!
You have to allocate space for your array. This is usually done using malloc:
// dynamically allocated array of 10 ints
int* dynamicIntArray = malloc(sizeof(int) * 10);
The problem here is that you don't know the number of elements beforehand. The general solution is to allocate a certain amount of space, and then reallocate more in case it is not sufficient (using realloc, for example). So your flow becomes:
Allocate some initial space using an arbitrary starting size: char** words = malloc(sizeof(char*) * 100);
Every time you add something to the array, check if you already have 100 elements. If so: words = realloc(words, lastSize * 2);
When you're done, free the memory: free(words);
Some notes
You mustn't increment words for the free to work correctly. So you have to either use a second pointer (char** pWords = words; then: pWords++) or use numeric indices instead of pointer arithmetic.
Allocating twice the original buffer's size is a common heuristic to keep the number of reallocations small.
In your case, you could in principle determine the number of words beforehand by counting spaces, but then you'll need to go over the input string twice.
As a side note: If all this seems daunting, keep in mind you don't have to handle this complexity when working in more high-level languages. In C++ for example, you can use a std::vector which will automatically grow as you add elements, and automatically free it's memory.
What you've done here is that take a pointer to pointer to character and then started working with it.
Why is it wrong ?
You have a pointer (or more precisely pointer to pointer) to char, but you never told it where to point to. Right now for all we know, it is pointing to some memory that you are not supposed to point to, or maybe some other junk memory location.
What do we do?
Use malloc, allocate memory to your pointer and then use it.
Keep in mind that when you have pointer to pointer to character, you'll have to malloc twice. The first one will make your pointer point to the placeholders for pointers that will point to characters. (Read again, it might make sense :P )
ie. if I do
char *ptr;
In this case, I'll do
ptr = malloc(sizeof(char) * 10) //considering your words are max length 10.
But since you want multiple words, hence you want a pointer that will point to an array of such pointers.
char **ptrSuper
ptrSuper = malloc(sizeof(char*) * 4) // considering you have 4 words.
Overall, this is what you'll have to do
char **ptr
ptr = malloc(sizeof(char*) * 4)
for(i=0 ; i<4 ; i++)
ptr[i] = malloc(sizeof(char)*10)
So I searched for one minute, and I found the following pic from some source that will make it a bit more lucid.
Link for Image.
As mentioned in the comments below, You don't need to do allocation for each word that you are to reverse. Since you'll get the reference of that from the character array you are passing. But just in case.

C strange error with strcpy and even stranger solution

I'm creating a program to open .txt files in a given directory, I have an array with all the absolute paths of the files inside the directory in question and I'm creating a function to extract and return the name of the files, the function is written as follows:
char *name(char *string) {
int i = strlen(string);
char *aux;
while(string[i-1] != '/'){
i--;
}
strcpy(aux, &string[i]);
return aux;
}
The above function is giving a Segmentation Fault error, but if I add the following line " int j = 0;" before the declaration of aux the mistake is gone, the new and working code is
char *name(char *string) {
int i = strlen(string);
int j = 0;
char *aux;
while(string[i-1] != '/'){
i--;
}
strcpy(aux, &string[i]);
return aux;
}
input: C:\test\a.txt
output: a.txt
Why the addition of "int j = 0;" solves the problem? I'm stuck with that and can't continue because I don't know if this inconsistency might lead to bigger problems later, I'm thinking about writing my own function to copy the strings, but before that I really want to understand that error.
You never allocate aux. aux needs to point to a valid memory location before you attempt to copy anything to it.
Instead of char *aux, you need something like char *aux = malloc(i+1);. Note that i+1 is overkill because in your case aux will always be at least 3 characters shorter than string (it won't contain C:\), but you probably don't care for such small strings. Remember to free() the pointer once you're done with it.
Also, the reason you found it works by switching orders of declarations and/or adding a declaration is probably that you got lucky and somehow the location to which aux points to is valid (if you do just char *aux;, aux points to a random location). This is pure luck however, and is still invalid code even though it seems to work.
In the future, you might want to use a tool like Valgrind to help you diagnose memory problems. You should also read a tutorial on basic memory management and pointers in C.
Since it sounds like you are only interested in utilizing the filename portion of the string as parameter, another option is to use the portion of the string you already have.
Try: aux = &string[i]; instead of the strcpy.
This gives you a pointer into the part of the string you are interested in (namely, the last part after the final '/').
Secondly, ensure you have a '/' in all of your input strings, otherwise bad things will happen (i.e. your loop will go past the beginning of the string, likely encountering a segmentation fault at some point). It would be best to put a condition on the loop so that it doesn't continue beyond i = 1.
You don't allocate any memory to aux. You are trying to write to memory through an uninitialized pointer.

Issue with printing char *argv[n] after a function call in C

int readOptions(char *argv[]){
FILE * infile;
char line_buf[BUFSIZ];
int i = 0, j = 0 ;
infile = fopen("options","r");
if(!infile){
fprintf(stderr,"File Read failure\n");
exit(2);
}
while( i < 10 && fgets(line_buf,sizeof(line_buf),infile)!=0){
printf("Line buf : %s",line_buf);
argv[i] = line_buf;
i++;
}
}
int main(){
int j ;
char *options[10];
for(j = 0 ; j< 10 ; j++){
options[j] = malloc(len * sizeof (char));
}
readOptions(options);
for(j=0; j<10 ; j++)
printf("%s %d\n",options[j], j );
}
The problem is that I always see - the program print only the last line read in the file. Where is the mistake ? and am I missing any important pointer concept with this code ?
Every element of argv points to the same line_buf. Use strdup() to create new strings instead (which you will later have to free).
A char* is not a string. It is a pointer. In your loop, you set each char* in your char*[] to point at the beginning of the line_buf array. Thus, each refers to the same data (which, furthermore, is no longer available after returning from the function; you get undefined behaviour at this point and you're just "lucky" - actually very unlucky, because it makes it harder to diagnose the problem - that it seems to "work" as well as it does.)
There is no real string type in C. You must set up separate chunks of memory that will hold the characters, and point at those chunks. If you allocate them dynamically, you will also have to free them. If you want to be able to resize them, or in general handle things of unknown size, that's also on you.
You've written the code to allocate some space, but you don't copy the data into the space - instead, you repoint the pointers at the local buffer. As noted, we use strcpy to copy from one buffer to another. But there's no real point in doing that, if you're going to pass in allocated buffers and limit yourself to their sizes anyway; instead, just fgets directly into the buffers pointed at by the pointers in the argv array, instead of into a local one.
The argv[i]s are not strings in and of themselves, but pointers to areas of memory that contain text strings. That's what you're doing when you say options[j] = malloc(...) -- you're setting aside an area of memory for that pointer to point at. But that area of memory is just an area of memory, it doesn't have anything to do with the pointer, except that the pointer happens to be pointing at it.
So when you say argv[i] = line_buf, that doesn't mean copy the string itself. It means change the pointer, such that argv[i] now points to the same area of memory where line_buf[] starts. Then in the next iteration of your for loop, you overwrite that same area of memory. By the end of the loop, all ten of your pointers are pointing to line_buf, which contains the last line of your file, which is why you get ten copies of that line.
(What's also worth noting is that once readOptions() returns, that area of memory that all your pointers are pointing to is considered undefined, because line_buf[] only "exists" within the readOptions() function. It's only through luck that your program is printing the last line ten times, rather than printing garbage or crashing.)
Now, to copy a string from one place to another, you can use the strcpy() function, which can be added to your program by putting #include <string.h> at the top. You'd then write strcpy(argv[i], line_buf).
Here's what a simple version of strcpy() would look like, so you can see what it's doing:
char *strcpy(char *dest, char *source) {
int i = 0;
while (source[i] != '\0') { /* a zero (or "null") byte means end of string */
dest[i] = source[i];
i=i+1;
}
dest[i] = source[i];
return dest;
}
Notice that strcpy() doesn't have any way of knowing how much space there is to copy into! If you don't have enough space available, it will go right past the end of your space and into who knows what memory areas, which may cause your program to crash or behave strangely. This is called a buffer overrun, and it's one of the most common security errors. So make sure you have enough space before you call strcpy().

Resources