I am trying to input a list of strings. The list may vary in length, so I try to use dynamic allocation. Each string has 20 chars max. The list ends with a single dot. I have been working on it for some time now, but I keep getting a segmentation fault and I am not sure why. I guess the error is in my use of realloc / malloc, but I just cannot see what exactly I am doing wrong. The code block is part of a larger program, but I singled out this block and am trying to make it work. It works fine for a "list" of one word followed by a dot. As soon as I try to read a list of two or more strings, I get the segmentation error.
Any help would be great, thanks!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **resizeDynamicArray(char **arr, int newSize){
char **ptr = realloc(arr, newSize*sizeof(char*));
if (ptr==NULL) {
printf("Error: memory allocation failed.\n");
exit(-1);
}
return ptr;
}
int input (char ***seq){
int len = 0;
char string[21];
*seq=NULL;
do{
scanf("%s", string);
if (string[0] != '.'){
*seq = resizeDynamicArray(*seq, (len+1));
*seq[len] = malloc(sizeof(char[21]));
strcpy((*seq)[len], string);
len++;
}
} while (string[0] != '.');
return len;
}
int main(int argc, char *argv[]) {
int length;
char **words;
length = input(&words);
for (int i=0; i<length; ++i){
printf("%s\n", words[i]);
}
for (int i=0; i<length; ++i){
free(words[i]);
}
free(words);
return 0;
}
Change the following line:
*seq[len] = malloc(sizeof(char[21]));
to:
(*seq)[len] = malloc(sizeof(char[21]));
There is an extra level of indirection that needs to be dereferenced before you can index into the top-level array.
Related
I'm trying to write a function that split strings and is not allowed to use strtok.
Assume the string only have spaces between the words, and only one space between each pair of words.
I allocate each word and words of array on heap. However, The result of running the program is : Segmentation fault(core dumped) . Any help is appreciated.
#include <stdio.h>
#include<string.h>
#include <stdlib.h>
void displayWords(char *wordArray[], int numWords){
if (wordArray != NULL){
int i;
for (i=0; i<numWords;i++)
printf("%i. \"%s\"\n", i+1, wordArray[i]);
}
}
int countSpaces(char theString[]) {
int spaces = 0, len = strlen(theString);
for(int i=0;i < len;i++) {
if(theString[i] == ' ') {
spaces++;
}
}
return spaces;
}
char** splitString(char theString[], int *arraySize){
int i,x,y=0;
int start,end=0; //index;
char *word; //create an array with char as elements
char **words;//create an array with words as its elements
int stringLen=strlen(theString);
int numOfWords = countSpaces(theString)+1;
*arraySize = numOfWords;
word = (char*)malloc(stringLen+1);
words = (char**)malloc(numOfWords);
for(x=0;x<=stringLen;x++){
if (theString[x]==' ' || theString[x]=='\0'){
for(x=start;x<=end;x++){
word[y]=theString[x];
y++;
strcpy(words[i],word);//copy the substring into words
words[i] = (char*)malloc(strlen(word)+1);
i++;
free(word);
start=end+2; //next start is the element right after the space
end=end+2; //do the same thing on end index
}
}
else{ //when theString[x] != ' '
end++;
}
}
words[*arraySize]=='\0';
return words;
}
int main(){
int size;
char test[]="it is a cat";
char **words = splitString(test, &size);
displayWords(words, size);
return 0;
}
words = (char**)malloc(numOfWords);
This should be :
words = malloc(numOfWords*sizeof(char*));
If you donot specify the sizeof(char*), required amount of memory might not be allocated. So, you may end up accessing unallocated location. This gives you segmentation fault. There is no need to cast the output of malloc
for(x=0;x<=stringLen;x++)
This should be :
for(x=0;x<stringLen;x++)
for(x=start;x<=end;x++)
start has not been initialized. (int start,end=0; )
While adding string to my pointer's array, it is being overwriten by the last one. Could anyone tell me, where's my mistake?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main (){
int ile = 3;
const char * slowa[ile];
for(int j = 0; j < ile; j++){
char string[30];
gets(string);
slowa[j] = string;
printf ("%s dodalem pierwsza\n",string);
}
for (int i = 0; i < ile; i++) {
printf ("%s numer %d\n",slowa[i],i);
}
return 0;
}
The answer is in the following two lines of code:
char string[30];
...
slowa[j] = string;
The assignment sets slowa[j] to the address of the same buffer, without making a copy. Hence, the last thing that you put in the buffer would be referenced by all elements of slowa[] array, up to position of j-1.
In order to fix this problem, make copies before storing values in slowa. You can use non-standard strdup, or use malloc+strcpy:
char string[30];
gets(string);
slowa[j] = malloc(strlen(string)+1);
strcpy(slowa[j], string);
In both cases you need to call free on all elements of slowa[] array to which you have assigned values in order to avoid memory leaks.
You're always pointing to array of chars which is stack variable it's locally allocated only in scope of function, possibly each declaration of string will be on the same address as previous iteration in your loop. You could either instead of using array of chars allocate memory each loop iteration or use array and then using i.e strdup allocate memory for your new string like
slowa[j] = strdup(string) :
As others have said, you need to create copies of the strings, otherwise you set the strings to the same address, and therefore they just overwrite each other.
Additionally, I think using fgets over gets is a much safer approach. This is because gets is very prone to buffer overflow, whereas with fgets, you can easily check for buffer overflow.
This is some code I wrote a while ago which is similar to what you are trying to achieve:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PTRS 3
#define STRLEN 30
int
string_cmp(const void *a, const void *b) {
const char *str1 = *(const char**)a;
const char *str2 = *(const char**)b;
return strcmp(str1, str2);
}
int
main(void) {
char *strings[PTRS];
char string[STRLEN];
int str;
size_t len, i = 0;
while (i < PTRS) {
printf("Enter a string: ");
if (fgets(string, STRLEN, stdin) == NULL) {
fprintf(stderr, "%s\n", "Error reading string");
exit(EXIT_FAILURE);
}
len = strlen(string);
if (string[len-1] == '\n') {
string[len-1] = '\0';
} else {
break;
}
strings[i] = malloc(strlen(string)+1);
if (strings[i] == NULL) {
fprintf(stderr, "%s\n", "Cannot malloc string");
exit(EXIT_FAILURE);
}
strcpy(strings[i], string);
i++;
}
qsort(strings, i, sizeof(*strings), string_cmp);
printf("\nSuccessfully read strings(in sorted order):\n");
for (str = 0; str < i; str++) {
printf("strings[%d] = %s\n", str, strings[str]);
free(strings[str]);
strings[str] = NULL;
}
return 0;
}
I would like to create an array of string variables, and the number of elements is depends on the user's input. For example, if the user's input is 3, then he can input 3 strings. Let's say "aaa", "bbb" and "ccc". They are stored by the same pointer to char(*ptr) but with different index.
code:
int main()
{
int t;
scanf("%d", &t);
getchar();
char *ptr = malloc(t*sizeof(char));
int i;
for(i=0;i<t;i++)
{
gets(*(ptr[i]));
}
for(i=0;i<t;i++)
{
puts(*(ptr[i]));
}
return 0;
}
t is the number of elements, *ptr is the pointer to array. I would like to store "aaa", "bbb" and "ccc" in ptr[0], ptr[1] and ptr[2]. However, errors have been found in gets and puts statement and i am not able to work out a solution. Would someone give a help to me? Thank you!
You shouldn't use gets(), which has unavoidable risk of buffer overrun, deprecated in C99 and deleted from C11.
Only one character can be stored in char. If the maximum length of strings to be inputted is fixed, you can allocate an array whose elements are arrays of char. Otherwise, you should use an array of char*.
Try this (this is for former case):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* the maximum length of strings to be read */
#define STRING_MAX 8
int main(void)
{
int t;
if (scanf("%d", &t) != 1)
{
fputs("read t error\n", stderr);
return 1;
}
getchar();
/* +2 for newline and terminating null-character */
char (*ptr)[STRING_MAX + 2] = malloc(t*sizeof(char[STRING_MAX + 2]));
if (ptr == NULL)
{
perror("malloc");
return 1;
}
int i;
for(i=0;i<t;i++)
{
if (fgets(ptr[i], sizeof(ptr[i]), stdin) == NULL)
{
fprintf(stderr, "read ptr[%d] error\n", i);
return 1;
}
/* remove newline character */
char *lf;
if ((lf = strchr(ptr[i], '\n')) != NULL) *lf = '\0';
}
for(i=0;i<t;i++)
{
puts(ptr[i]);
}
free(ptr);
return 0;
}
You can use this code which is given below because string array is like char 2D array so use can use pointer of pointer and when you allocate memory at run time by malloc then you need to cast into pointer to pointer char type.
int main()
{
int t;
scanf("%d", &t);
char **ptr = (char **)malloc(t*sizeof(char));
int i,j;
for( i=0;i<t;i++)
{
scanf("%s",ptr[i]);
}
for(i=0;i<t;i++)
{
puts(ptr[i]);
}
return 0;
}
Here is an example, of a clean if slightly memory inefficient way to handle this. A more memory efficient solution would use one string of MAX_LINE_LENGTH and copy to strings of precise lengths.. which is why one contiguous block of memory for the strings is a bad idea.
The asserts also just demonstrate where real checks are needed as malloc is allowed to fail in production where asserts do nothing.
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
#define MAX_LINE_LENGTH 2048
int
main(void) {
int tot, i;
char **strheads; /* A pointer to the start of the char pointers */
if (scanf("%d\n", &tot) < 1)
return (1);
strheads = malloc(tot * sizeof (char *));
assert(strheads != NULL);
/* now we have our series of n pointers to chars,
but nowhere allocated to put the char strings themselves. */
for (i = 0; i < tot; i++) {
strheads[i] = malloc(sizeof (char *) * MAX_LINE_LENGTH);
assert(strheads[i] != NULL);
/* now we have a place to put the i'th string,
pointed to by pointer strheads[i] */
(void) fgets(strheads[i], MAX_LINE_LENGTH, stdin);
}
(void) printf("back at ya:\n");
for (i = 0; i < tot; i++) {
fputs(strheads[i], stdout);
free(strheads[i]); /* goodbye, i'th string */
}
free(strheads); /* goodbye, char pointers [0...tot] */
return (0);
}
I have a program that reverses a string from an input of a variable length character array. The function returns a variable length character array and is printed. When I print the output, I do get the reversed string, but there are garbage characters appended to it in my console print.
Is this a "legal" operation in terms of returning to buffers? Can someone please critique my code and suggest a better alternative if it is not the right approach?
Thanks.
#include <stdio.h>
#include <stdlib.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
int i=0;
int j=0;
char *return_string;
char filled_buffer[16];
while (input_string[i]!='\0')
i++;
while (i!=0)
{
filled_buffer[j]=input_string[i-1];
i--;
j++;
}
return_string=filled_buffer;
printf("%s", return_string);
return return_string;
}
int main (void)
{
char *returned_string;
returned_string=reverse_string("tasdflkj");
printf("%s", returned_string);
return 1;
}
This is my output from Xcode - jklfdsat\347\322̲\227\377\231\235
No, it isn't safe to return a pointer to a local string in a function. C won't stop you doing it (though sometimes the compiler will warn you if you ask it to; in this case, the local variable return_string prevents it giving the warning unless you change the code to return filled_buffer;). But it is not safe. Basically, the space gets reused by other functions, and so they merrily trample on what was once a neatly formatted string.
Can you explain this comment in more detail — "No, it isn't safe..."
The local variables (as opposed to string constants) go out of scope when the function returns. Returning a pointer to an out-of-scope variable is undefined behaviour, which is something to be avoided at all costs. When you invoke undefined behaviour, anything can happen — including the program appearing to work — and there are no grounds for complaint, even if the program reformats your hard drive. Further, it is not guaranteed that the same thing will happen on different machines, or even with different versions of the same compiler on your current machine.
Either pass the output buffer to the function, or have the function use malloc() to allocate memory which can be returned to and freed by the calling function.
Pass output buffer to function
#include <stdio.h>
#include <string.h>
int reverse_string(char *input_string, char *buffer, size_t bufsiz);
int reverse_string(char *input_string, char *buffer, size_t bufsiz)
{
size_t j = 0;
size_t i = strlen(input_string);
if (i >= bufsiz)
return -1;
buffer[i] = '\0';
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", buffer);
return 0;
}
int main (void)
{
char buffer[16];
if (reverse_string("tasdflkj", buffer, sizeof(buffer)) == 0)
printf("%s\n", buffer);
return 0;
}
Memory allocation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *reverse_string(char *input_string);
char *reverse_string(char *input_string)
{
size_t j = 0;
size_t i = strlen(input_string) + 1;
char *string = malloc(i);
if (string != 0)
{
string[--i] = '\0';
while (i != 0)
{
string[j] = input_string[i-1];
i--;
j++;
}
printf("%s\n", string);
}
return string;
}
int main (void)
{
char *buffer = reverse_string("tasdflkj");
if (buffer != 0)
{
printf("%s\n", buffer);
free(buffer);
}
return 0;
}
Note that the sample code includes a newline at the end of each format string; it makes it easier to tell where the ends of the strings are.
This is an alternative main() which shows that the allocated memory returned is OK even after multiple calls to the reverse_string() function (which was modified to take a const char * instead of a plain char * argument, but was otherwise unchanged).
int main (void)
{
const char *strings[4] =
{
"tasdflkj",
"amanaplanacanalpanama",
"tajikistan",
"ablewasiereisawelba",
};
char *reverse[4];
for (int i = 0; i < 4; i++)
{
reverse[i] = reverse_string(strings[i]);
if (reverse[i] != 0)
printf("[%s] reversed [%s]\n", strings[i], reverse[i]);
}
for (int i = 0; i < 4; i++)
{
printf("Still valid: %s\n", reverse[i]);
free(reverse[i]);
}
return 0;
}
Also (as pwny pointed out in his answer before I added this note to mine), you need to make sure your string is null terminated. It still isn't safe to return a pointer to the local string, even though you might not immediately spot the problem with your sample code. This accounts for the garbage at the end of your output.
First, returning a pointer to a local like that isn't safe. The idiom is to receive a pointer to a large enough buffer as a parameter to the function and fill it with the result.
The garbage is probably because you're not null-terminating your result string. Make sure you append '\0' at the end.
EDIT: This is one way you could write your function using idiomatic C.
//buffer must be >= string_length + 1
void reverse_string(char *input_string, char* buffer, size_t string_length)
{
int i = string_length;
int j = 0;
while (i != 0)
{
buffer[j] = input_string[i-1];
i--;
j++;
}
buffer[j] = '\0'; //null-terminate the string
printf("%s", buffer);
}
Then, you call it somewhat like:
#define MAX_LENGTH 16
int main()
{
char* foo = "foo";
size_t length = strlen(foo);
char buffer[MAX_LENGTH];
if(length < MAX_LENGTH)
{
reverse_string(foo, buffer, length);
printf("%s", buffer);
}
else
{
printf("Error, string to reverse is too long");
}
}
My program reads a file specified in the argument and prints out each string and its frequency inside the file.
The program works for this file: http://www.cse.yorku.ca/course/3221/dataset1.txt
but not this file: http://www.cse.yorku.ca/course/3221/dataset2.txt.
It gives Segmentation fault (core dumped) error for the second file.
What could be wrong? Please help!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char word[101];
int freq;
} WordArray;
int main(int argc, char *argv[])
{
WordArray *array = malloc(sizeof(WordArray));
FILE *file;
int i = 0;
file = fopen(argv[1], "r");
char *str = (char*) malloc (108);
while(fgets(str, 100, file) != NULL)
{
int pos = 0;
char *word = malloc (100);
while (sscanf(str, "%s%n", word, &pos ) == 1)
{
int j;
for (j = 0; j < i; j++)
{
if (strcmp(array[j].word, word) == 0)
{
array[j].freq = array[j].freq + 1;
break;
}
}
if (j==i)
{
array = (WordArray *) realloc (array, sizeof(WordArray) * (i+1));
strcpy(array[i].word, word);
array[i].freq = 1;
i++;
}
str += pos;
}
}
fclose(file);
int k;
for (k=0; k<i; k++)
{
printf("%s %d\n", array[k].word, array[k].freq);
}
return 0;
}
Several problems:
You increment str as part of the second loop and don't reset it. I think this means your program is slowly walking through memory.
You fail to free word - probably better to allocate it outside the loop and on the stack but that won't cause a crash unless you input is huge and you run out of memory.
You don't need to cast result of malloc for modern compilers (yes, it used to be needed).
May want to check the results of malloc and realloc for safety.
I assume the first item is your problem.