Working on a program to split strings into smaller strings with trailing whitespace removed, in an attempt to fully get knowledge of pointers in my head. However, when I try and do pointer arithmetic on a array of pointers, I keep getting a segmentation fault (tested and worked out this was the specific line where the fault occurs); Here's my code:
int enfold(char* to_enfold, long unsigned line_length, char** enfolded) {
int k;
long unsigned char_num = strlen(to_enfold);
if (char_num < line_length) {
for (k = 0; k <= sizeof(to_enfold); k++)
enfolded[0][k] = to_enfold[k];
printf("TOO SHORT\n");
enfolded[0][k] = '\n';
return 1;
}
else {
int i = LINE_LENGTH-1;
while ( to_enfold[i] == ' ' ||
to_enfold[i] == '\t' ||
to_enfold[i] == '\n' ||
i == 0) {
printf("%d\n", i);
i--;
}
for (k = 0; k <= i; k++)
enfolded[0][k] = to_enfold[k];
enfolded[0][k] = '\n';
return 1 + enfold((to_enfold+LINE_LENGTH), line_length, (enfolded+1));
}
}
The problem is with the recursion, which causes a segmentation fault for using arithmetic for (enfolded+1), though not if we overwrite using enfolded. Is there a problem with using pointer arithmetic on pointers to pointers.
One problem is the use of sizeof() in the code:
if (char_num < line_length) {
for (k = 0; k <= sizeof(to_enfold); k++)
enfolded[0][k] = to_enfold[k];
That should be strlen(), except you don't call strlen() in the condition of a loop, and in any case the loop should be written:
strcpy(enfolded[0], to_enfold);
However, that isn't the part of the code where you are seeing the problem. Since you've not shown us how the memory is allocated for enfolded, it is hard to know what you've done, but there's a very good chance that you've not allocated the memory correctly. You should have pre-allocated not only the array of pointers, but also the array that each of the pointers points at (since you don't allocate the space in this code). Alternatively, you need to allocate the requisite space here. You also have not told this function how many pointers are in the array. Fiendishly crafted input can therefore easily cause you to write out of bounds. You need to know how much space there is in the array of pointers.
So, I think you should:
Allocate the strings in this function.
Make sure you free them all when you're done.
Make sure you know how many strings can be stored in the array.
If you're on a system with valgrind then use that to guide you through memory allocation and release.
Please read up on how to create an SSCCE (Short, Self-Contained, Correct Example). One reason why people may not have jumped in to help is that your code self-evidently is not an SSCCE; there is no main() function. Here is an approximation to an SSCCE that works. The main change I made was to ensure that the strings are null terminated; I'm fairly sure that was a large part of your problem.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static
int enfold(char *to_enfold, size_t line_length, char **enfolded)
{
size_t k;
size_t char_num = strlen(to_enfold);
if (char_num < line_length)
{
for (k = 0; k <= char_num; k++)
enfolded[0][k] = to_enfold[k];
printf("TOO SHORT\n");
enfolded[0][k] = '\n';
enfolded[0][k+1] = '\0';
return 1;
}
else
{
size_t i = line_length - 1;
while (to_enfold[i] == ' ' ||
to_enfold[i] == '\t' ||
to_enfold[i] == '\n' ||
i == 0)
{
printf("%zu\n", i);
i--;
}
for (k = 0; k <= i; k++)
enfolded[0][k] = to_enfold[k];
enfolded[0][k] = '\n';
enfolded[0][k+1] = '\0';
return 1 + enfold((to_enfold + line_length), line_length, (enfolded + 1));
}
}
int main(void)
{
enum { SIZE = 100 };
char *enfolded[SIZE];
for (int i = 0; i < SIZE; i++)
enfolded[i] = malloc(sizeof(char) * SIZE);
char line[4096];
while (fgets(line, sizeof(line), stdin) != 0)
{
int n = enfold(line, 8, enfolded);
assert(n < SIZE);
for (int i = 0; i < n; i++)
printf("%d: <<%s>>\n", i, enfolded[i]);
printf("Parts: %d\n", n);
}
for (int i = 0; i < SIZE; i++)
free(enfolded[i]);
return 0;
}
Sample run:
$ ./mem <<< "Hello, how are you today?"
TOO SHORT
0: <<Hello, h
>>
1: <<ow are y
>>
2: <<ou today
>>
3: <<?
>>
Parts: 4
$
Checking with valgrind gives the code a clean bill of health. However, you might need to think about what happens if you have 30 blanks in the middle of the string (my strong suspicion is that you'll run into problems, but I've not proven that).
Compilation with GCC 4.8.2 on Mac OS X 10.9.1 Mavericks:
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror mem.c -o mem
I pretty much don't compile with less stringent compilation options than that (unless it needs to be C99 or, perish the thought, C89 code). Note that the code does use some C99 features, notably declarations of variables in the for loop.
Related
I am doing a school exercise and it's asking us to split a string(character array) into multiple character arrays. A string input like this
"asdf qwerty zxcv"
should result in an array of characters arrays like this
"asdf","qwerty","zxcv"
While I am testing the code, no matter what strings I entered as the argument of my function, the first string printed out would always be some random characters, while the rest are as expected.
"02�9�","qwerty","zxcv"
Besides, my code worked fine in online compilers, which I saved here. I also tested in OnlineGDB, in which the code worked pretty well too.
This is my code with the main function:
#include <stdio.h>
#include <stdlib.h>
int is_separator(char c)
{
if (c == '\n' || c == '\t' || c == ' ' || c == '\0')
{
return (1);
}
else
{
return (0);
}
}
int ct_len(int index, char *str)
{
int i;
i = index;
while (!(is_separator(str[index])))
{
index++;
}
return (index - i);
}
int ct_wd(char *str)
{
int count;
int i;
i = 0;
count = 0;
while (str[i])
{
if (is_separator(str[i]))
count++;
i++;
}
return (count + 1);
}
char **ft_split_whitespaces(char *str)
{
char **tab;
int i;
int j;
int k;
i = 0;
j = 0;
tab = malloc(ct_wd(str));
while (str[j])
{
k = 1;
while (is_separator(str[j]))
j++;
*(tab + i) = (char *)malloc(sizeof(char) * ((ct_len(j, str) + 1)));
while (!(is_separator(str[j])))
{
tab[i][k - 1] = str[j++];
k++;
}
tab[i++][k - 1] = '\0';
}
tab[i] = 0;
return (&tab[0]);
}
int main(void)
{
char** res;
for (res = ft_split_whitespaces("asdf qwerty zxcv"); *res != 0; res++)
{
printf("'%s',", *res);
}
return (0);
}
One hint is that the output of the first array is changing, which suggests that there might be some problems with my memory allocation. However, I am not sure about it. If you can help me find out where the bug is, I would be really appreciative of your help. Thank you very much for reading.
this
tab = malloc(ct_wd(str));
to this
tab = malloc(ct_wd(str) * sizeof(char *));
also you wight want to consider using valgrind, which should provide a fair indication of where the corruption is. essentially ct_wd(str) function is the main culprit along with malloc statement after that. you might want to take a closer look at how much memory you are allocating and how much actually using. as mentioned valgrind should assist you better.
valgrind --tool=memcheck --leak-check=full --track-origins=yes <executalbe>
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am writing a program in C that replaces a number in a char* called "template" with a string, but I continually get a Segmentation Fault: 11 error.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
char *rep_str(const char *s, const char *old, const char *new1){
char *ret;
int i, count = 0;
int newlen = strlen(new1);
int oldlen = strlen(old);
for (i = 0; s[i] != '\0'; i++){
if (strstr(&s[i], old) == &s[i]){
count++;
i += oldlen - 1;
}
}
ret = (char*)malloc(i + count * (newlen - oldlen));
if (ret == NULL)
exit(EXIT_FAILURE);
i = 0;
while (*s){
if (strstr(s, old) == s){ //compare the substring with the newstring
strcpy(&ret[i], new1);
i += newlen; //adding newlength to the new string
s += oldlen;//adding the same old length the old string
} else {
ret[i++] = *s++;
}
}
ret[i] = '\0';
return ret;
}
char* madlib_by_numbers(char* temp, int word_count, char* words[]){
char* numbers[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
int tempSize = strlen(temp);
for (int i = 0; i < tempSize; i++){
if (isdigit(temp[i])){
for (int j = 0; j < (sizeof(numbers) / sizeof(char*)); j++){
temp = rep_str(temp, numbers[j], words[j]); //it makes it to this line, but never gets to assert()
}
}
}
return temp;
}
int main() {
char* temp1 = "The 1 0 likes to 2 in the moonlight.";
char* words[] = {"git", "brilliant", "swim"};
char* result = "The brilliant git likes to swim in the moonlight.";
int stringLength = strlen(result);
char* test = madlib_by_numbers(temp1, 3, words);
assert(strncmp(test, result, stringLength) == 0);
free(test);
return 0;
}
and when I run the debugger, it simply says: Segmentation Fault: 11
What i just want to understand is where the segmentation fault error is coming from, I have the suspicion one of my loops is running too many times.
There are a few issue with your code. However, the direct answer to your question is in this loop:
for (int j = 0; j < (sizeof(numbers) / sizeof(char*)); j++){
temp = rep_str(temp, numbers[j], words[j]);
}
You are calling rep_str for every digit while you mean call rep_str only if the digit in temp matches the corresponding digit in numbers. So add this conditional if(strcmp(temp,numbers[j]) == 0) right before the line temp=.... Then it'll solve your current problem.
The segfault is caused because there are only three elements in the words array. Your old loop indexes from 0 to 9 and fails when j=3, out of bound.
Also, delete the free() at the end of your program. test was never allocated and will cause a core dump.
ret = (char*)malloc(i + count * (newlen - oldlen));
There are a few problems with this line of code.
For a start, don't cast malloc (or any void * that you're assigning to a variable of different pointer type, or vice versa).
If you intended to allocate space to store a string, where's the string-terminating '\0' going to go? You need to realise that for an empty old string, this will be malloc(0) and zero bytes is not enough to store an empty string.
There's also a problem if you expect that old may be a substring of new (for example, you're replacing "0" with "hell0"). You'll need to tweak your algorithm to handle this problem. I'll leave that as a challenge for you to attempt :)
for (int i = 0; i < tempSize; i++){
if (isdigit(temp[i])){
for (int j = 0; j < (sizeof(numbers) / sizeof(char*)); j++){
temp = rep_str(temp, numbers[j], words[j]); //it makes it to this line, but never gets to assert()
}
}
}
users previous answer highlighted this code correctly, but not for the right reason... and so the solution he/she presented is wrong.
isdigit(temp[i]) may also cause segfaults for some inputs. I recommend using isdigit((unsigned char) temp[i]) instead, in this case.
It's not valid to access words[j] where word_count is 3 and j is greater or equal to 3; you're accessing that array out of bounds.
You also need to be careful to free any memory you *alloc (while simultaneously not freeing memory that you don't *alloc). Forgetting to do the former won't cause crashes, but your program won't run happily; it'll use heaps of memory.
Consider something like this, instead:
temp = strdup(temp);
if (temp == NULL) {
exit(EXIT_FAILURE);
}
for (int i = 0; i < tempSize; i++){
if (isdigit((unsigned char) temp[i])){
for (int i = min(word_count, sizeof(numbers) / sizeof(char*)), j = 0; j < i; j++){
char *new = rep_str(temp, numbers[j], words[j]);
free(temp);
temp = new;
}
}
}
So I'm trying to load the s-records from a .s19 file into memory for an assignment I'm working on, and its working. However, when I delete an unused array from my code, everything stops working and crashes.
The unused array is:
char test[65536];
And this is the loader I've written:
void loader(FILE * srec)
{
char instring[SREC_LEN];
char test[65536]; // This isn't used, but the program crashes without it for some reason
int i=0;
int j=0, k,l;
while (fgets(instring, SREC_LEN, srec) != NULL)
{
while(instring[i] != '\n') // Counts the characters in the s-record
{
i++;
}
j = j+i;
for(k=0;k<=i;k++) // Puts the records into memory
{
memory[l] = instring[k];
l++;
}
l = j;
}
#ifdef DEBUG
printf("MEMORY: %s",memory);
#endif // DEBUG
}
If you could help me to understand why this is happening, I would appreciate it.
Your code has undefined behavior, it only works by sheer luck:
fgets() may return without writing a newline character into the buffer if EOF is reached prematurely. So you should at least account for that in your loop. Also you never reset i to 0, which you should. Change this:
while(instring[i] != '\n') // Counts the characters in the s-record
{
i++;
}
to:
i = 0;
while(instring[i] != '\n' && instring[i] != '\0') // Counts the characters in the s-record
{
i++;
}
l is never initialized; you are probably writing out of bounds in memory. Initialize l to 0:
int j = 0, k, l = 0;
(I assume that memory is large enough to hold everything).
It also looks to me like you want for(k = 0; k < i; k++) rather than for(k = 0; k <= i; k++), since i is the count of characters you want to copy.
You might want to use memcpy() instead.
I am experiencing an issue where the invocation of realloc seems to modify the contents of another string, keyfile.
It's supposed to run through a null-terminated char* (keyfile), which contains just above 500 characters. The problem, however, is that the reallocation I perform in the while-loop seems to modify the contents of the keyfile.
I tried removing the dynamic reallocation with realloc and instead initialize the pointers in the for-loop with a size of 200*sizeof(int) instead. The problem remains, the keyfile string is modified during the (re)allocation of memory, and I have no idea why. I have confirmed this by printing the keyfile-string before and after both the malloc and realloc statements.
Note: The keyfile only contains the characters a-z, no digits, spaces, linebreaks or uppercase. Only a text of 26, lowercase letters.
int **getCharMap(const char *keyfile) {
char *alphabet = "abcdefghijklmnopqrstuvwxyz";
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++) {
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
}
int letter;
int count = 0;
unsigned char c = keyfile[count];
while (c != '\0') {
int arr_count = charmap[c][0];
arr_count++;
charmap[c] = realloc(charmap[c], (arr_count+1)*sizeof(int));
charmap[c][0] = arr_count;
charmap[c][arr_count] = count;
c = keyfile[++count];
}
// Just inspecting the results for debugging
printf("\nCHARMAP\n");
for (int i = 0; i < 26; i++) {
letter = (int) alphabet[i];
printf("%c: ", (char) letter);
int count = charmap[letter][0];
printf("%d", charmap[letter][0]);
if (count > 0) {
for (int j = 1; j < count+1; j++) {
printf(",%d", charmap[letter][j]);
}
}
printf("\n");
}
exit(0);
return charmap;
}
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
You are writing beyond the end of your charmap array. So, you are invoking undefined behaviour and it's not surprising that you are seeing weird effects.
You are using the character codes as an index into the array, but they do not start at 0! They start at whatever the ASCII code for a is.
You should use alphabet[i] - 'a' as your array index.
The following piece of code is a source of troubles:
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++)
charmap[...] = ...;
If sizeof(int) < sizeof(int*), then it will be performing illegal memory access operations.
For example, on 64-bit platforms, the case is usually sizeof(int) == 4 < 8 == sizeof(int*).
Under that scenario, by writing into charmap[13...25], you will be accessing unallocated memory.
Change this:
int **charmap = malloc(26*sizeof(int));
To this:
int **charmap = malloc(26*sizeof(int*));
Well, I've been at this forever and I know exactly where the fault is, but no clue how to fix it. I already know fgets and scanf would be better for this program, but I can't do that.
The program worked about 10 minutes ago, then I changed it and got a seg fault. Then I changed it back and still got a seg fault. Anyway, I'm sure the fresh eyes will see it right away. Have at it :D
PS: Please note my (lessthan) instead of < because I don't know how to properly leave those in my code examples still :(
#define WORDLENGTH 15
#define MAXLINE 1000
int main()
{
char *line[MAXLINE];
int i = 0;
int j;
int n;
char c;
for (n=0; c!=EOF; n++){
char *tmp = (char *) malloc(sizeof(char)*WORDLENGTH);
while ((c=getchar())!=' ')
tmp[i++]=c;
line[n]=tmp;
i=0;
printf("\n%s\n",line[n]); //
}
for(j = 0; j < n; j++){
printf("\n%s\n", line[j]);
free (line[j]);
}
return 0;
}
you are doing line[n++] = tmp. And then accessing line[n] after that. But line[n] hasn't been assigned.
To change it, you can print line[n-1] instead, but clearer would be:
line[n] = tmp;
i = 0;
printf(... line[n]);
and place the increment in the for statement instead i.e. for (n = 0; c != EOF; n++).
EDIT
This is a summary of what I would do:
Place the i=0 assignment at the start of the loop. Logically, it is an initialization of i and currently it is done in two places (at int i = 0; and after the assignment of line[n]). Both places are not near where one would expect an initialization of a variable used in the while loop to be.
Guard against nonsense input by checking that i does not exceed WORDLENGTH-1. Actually, I would probably code the inner while loop as a for loop on i like so:
for (i = 0; i < WORDLENGTH; i++) {
tmp[i] = getchar();
if (tmp[i] == ' ') break;
}
tmp[i] = 0;
or (in my character) for(i = 0; i < WORDLENGTH; ++i) if ((tmp[i] = getchar()) == ' ') break; followed by..
tmp[i] = 0 to NUL-terminate the string. Since malloc doesn't necessarily return a 0-filled memory block.
there are still bugs in the suggested solution !
malloc() can fail and return a NULL pointer
at the end of the for () the maximum i value is WORDLENGTH
so this assignment isn't correct ( out of bounds )
tmp[i]= 0;
Can fix both with
char *tmp = (char *) malloc( sizeof(char) * (WORDLENGTH + 1) );
if ( tmp == NULL ) // end of available memory
break;
moreover, it isn't clear if you allow EOF inside the last string.