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;
}
}
}
Related
(in C, using visual studio 2022 preview), I have to do a program that link two strings together. Here's what I did:
I wrote two for-loops to count characters of first string and second
string,
I checked (inside the link function if the pointers are null (first and second). If they are null, then "return NULL".
I created "char *result". this is a new string and this is the string to be returned. I allocated enough memory to store nprime, nsecond, and 1 more character (the zero terminator). I used a malloc.
then, I checked if result is null. if it's null then "return NULL".
then, I wrote 2 for-loops to perform the linking between the first string and the second string. And here I got a compiler warning (because I think it's in compile time not in debug time). buffer overrun, the writable size is
"nprime+nsecond+1" but 2 bytes might be written.
my theory is that the program is trying to write outside the result-array, so there could be a loss of data, I tried to edit my code, therefore I write "nprime+nsecond+2" instead but it doesn't work, and it keeps showing me the same buffer overrun error.
#include <stdlib.h>
char* link( const char* first, const char* second) {
size_t nprime = 0;
size_t nsecond = 0;
if (first == NULL) {
return NULL;
}
if (second == NULL) {
return NULL;
}
for (size_t i = 0; first[i] < '\0'; i++) {
nprime++;
}
for (size_t i = 0; second[i] < '\0'; i++) {
nsecond++;
}
char* result = malloc(nprime + nsecond + 1);
if (result == NULL) {
return NULL;
}
for (size_t i = 0; i < nprime; i++) {
result[i] = first[i];
}
for (size_t i = 0; i < nsecond; i++) {
result[nprime + i] = second[i];
}
result[nprime + nsecond] = 0;
return result;
}
this is the main:
int main(void) {
char s1[] = "this is a general string ";
char s2[] = "this is a general test.";
char* s;
s = link(s1, s2);
return 0;
}
The warning is given due to the wrong conditions you defined in the first 2 for loops. The right loops should be as follows:
for (size_t i = 0; first[i] != '\0'; i++) {
nprime++;
}
for (size_t i = 0; second[i] != '\0'; i++) {
nsecond++;
}
With the conditions you defined (i.e. first[i] < '\0') you are just counting how many chars in the given string have an ASCII code lower than the ASCII code of \0 and exit the loop as soon as you find a char not fulfilling such condition.
Since '\0' has ASCII value 0, your nprime and nsecond are never incremented, leading to a malloc with insufficient room for the chars you actually need.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 7 years ago.
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.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Improve this question
I need to create a fuction that returns a string. So far I have come up with this code:
#include <stdio.h>
#include <string.h>
#define SIZE 256
void str_reverse(char[SIZE], char[SIZE]);
int main(void)
{
char input[SIZE];
char output[SIZE];
printf("Please enter a string\n");
fgets(input, SIZE, stdin);
str_reverse(input, output);
printf("%s \n", output);
return 0;
}
void str_reverse(char in[SIZE], char out[SIZE])
{
int i = strlen(in);
int k = 0;
for(int j = i; j>= 0; j--)
{
out[k] = in[j];
k++;
}
}
However, whenever I try to run the code, I do not get an output. Does anyone have any idea what could be wrong
The problem is this line
int i = strlen(in);
Change it to
int i = strlen(in)-1;
strlen() returns the size of the string, hence when you start copying from in[], in[j] would be \0. which is getting stored in out[0].
Another change you need to do is put a \0 in the out[], just after the for loop
for(int j = i; j>= 0; j--)
{
out[k] = in[j];
k++;
}
out[k] = '\0';
Your "reversing" includes the null character too. So the reversed string's first character is '\0'. So the string is empty.
Change:
for(int j = i ; j>= 0; j--)
to
for(int j = i - 1; j>= 0; j--)
You should also check if the string length is zero before the for loop. Otherwise, i-1 will become SIZE_MAX and you'll have problems with that!
A simple condition such as the following in str_reverse() would handle that case:
size_t i = strlen(in);
int k = 0;
if (i<=1) return; //empty string or contains only 1 char char.
i starts out at strlen(in), so the first character you copy over tooutis the null byte at the end of the string. Every other character then appears after the null terminator, soout` is an empty string.
Start with i at strlen(in) - 1, then add the null terminator and the end of the loop:
int i = strlen(in) - 1; // start at the last non-null character
int k = 0;
for(int j = i; j>= 0; j--)
{
out[k] = in[j];
k++;
}
out[k] = '\0'; // add the null terminator
When k is 0 and j is i, you do the following:
out[0] = '\0';
Hence, out looks like an empty string when the function returns.
Use:
for(int j = i-1; j>= 0; j--)
{
out[k] = in[j];
k++;
}
out[k] = '\0';
In the function variable i is set the following way
int i = strlen(in);
So within the loop
for(int j = i; j>= 0; j--)
{
out[k] = in[j];
k++;
}
then j is equal to i then in[j] is equal to the terminating zero '\0' of string in. Thus array out starts from zero that is it will contain an empty string.
It is better to declare the function itself the following way
char * str_reverse( char *out, const char *in );
and to return from the function the pointer out. In this case you could write for example
puts( str_reverse( output, input ) );
Take into acount that function fgets usually also stores the new line character in the string. You should remove it. you can do it the following way
input[strcspn( input, "\n" )] = '\0';
Also instead of the type int in this statement
int i = strlen(in);
it is better to use type size_t.
size_t i = strlen(in);
It is the type of the return value of the function strlen. But in this case you should also to write the loop correctly.:)
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*));
I can't write a workable code for a function that deletes N characters from the string S, starting from position P. How you guys would you write such a function?
void remove_substring(char *s, int p, int n) {
int i;
if(n == 0) {
printf("%s", s);
}
for (i = 0; i < p - 1; i++) {
printf("%c", s[i]);
}
for (i = strlen(s) - n; i < strlen(s); i++) {
printf("%c", s[i]);
}
}
Example:
s: "abcdefghi"
p: 4
n: 3
output:
abcghi
But for a case like n = 0 and p = 1 it's not working!
Thanks a lot!
A few people have shown you how to do this, but most of their solutions are highly condensed, use standard library functions or simply don't explain what's going on. Here's a version that includes not only some very basic error checking but some explanation of what's happening:
void remove_substr(char *s, size_t p, size_t n)
{
// p is 1-indexed for some reason... adjust it.
p--;
// ensure that we're not being asked to access
// memory past the current end of the string.
// Note that if p is already past the end of
// string then p + n will, necessarily, also be
// past the end of the string so this one check
// is sufficient.
if(p + n >= strlen(s))
return;
// Offset n to account for the data we will be
// skipping.
n += p;
// We copy one character at a time until we
// find the end-of-string character
while(s[n] != 0)
s[p++] = s[n++];
// And make sure our string is properly terminated.
s[p] = 0;
}
One caveat to watch out for: please don't call this function like this:
remove_substr("abcdefghi", 4, 3);
Or like this:
char *s = "abcdefghi";
remove_substr(s, 4, 3);
Doing so will result in undefined behavior, as string literals are read-only and modifying them is not allowed by the standard.
Strictly speaking, you didn't implement a removal of a substring: your code prints the original string with a range of characters removed.
Another thing to note is that according to your example, the index p is one-based, not zero-based like it is in C. Otherwise the output for "abcdefghi", 4, 3 would have been "abcdhi", not "abcghi".
With this in mind, let's make some changes. First, your math is a little off: the last loop should look like this:
for (i = p+n-1; i < strlen(s); i++) {
printf("%c", s[i]);
}
Demo on ideone.
If you would like to use C's zero-based indexing scheme, change your loops as follows:
for (i = 0; i < p; i++) {
printf("%c", s[i]);
}
for (i = p+n; i < strlen(s); i++) {
printf("%c", s[i]);
}
In addition, you should return from the if at the top, or add an else:
if(n == 0) {
printf("%s", s);
return;
}
or
if(n == 0) {
printf("%s", s);
} else {
// The rest of your code here
...
}
or remove the if altogether: it's only an optimization, your code is going to work fine without it, too.
Currently, you code would print the original string twice when n is 0.
If you would like to make your code remove the substring and return a result, you need to allocate the result, and replace printing with copying, like this:
char *remove_substring(char *s, int p, int n) {
// You need to do some checking before calling malloc
if (n == 0) return s;
size_t len = strlen(s);
if (n < 0 || p < 0 || p+n > len) return NULL;
size_t rlen = len-n+1;
char *res = malloc(rlen);
if (res == NULL) return NULL;
char *pt = res;
// Now let's use the two familiar loops,
// except printf("%c"...) will be replaced with *p++ = ...
for (int i = 0; i < p; i++) {
*pt++ = s[i];
}
for (int i = p+n; i < strlen(s); i++) {
*pt++ = s[i];
}
*pt='\0';
return res;
}
Note that this new version of your code returns dynamically allocated memory, which needs to be freed after use.
Here is a demo of this modified version on ideone.
Try copying the first part of the string, then the second
char result[10];
const char input[] = "abcdefg";
int n = 3;
int p = 4;
strncpy(result, input, p);
strncpy(result+p, input+p+n, length(input)-p-n);
printf("%s", result);
If you are looking to do this without the use of functions like strcpy or strncpy (which I see you said in a comment) then use a similar approach to how strcpy (or at least one possible variant) works under the hood:
void strnewcpy(char *dest, char *origin, int n, int p) {
while(p-- && *dest++ = *origin++)
;
origin += n;
while(*dest++ = *origin++)
;
}
metacode:
allocate a buffer for the destination
decalre a pointer s to your source string
advance the pointer "p-1" positions in your source string and copy them on the fly to destination
advance "n" positions
copy rest to destination
What did you try? Doesn't strcpy(s+p, s+p+n) work?
Edit: Fixed to not rely on undefined behaviour in strcpy:
void remove_substring(char *s, int p, int n)
{
p--; // 1 indexed - why?
memmove(s+p, s+p+n, strlen(s) - n);
}
If your heart's really set on it, you can also replace the memmove call with a loop:
char *dst = s + p;
char *src = s + p + n;
for (int i = 0; i < strlen(s) - n; i++)
*dst++ = *src++;
And if you do that, you can strip out the strlen call, too:
while ((*dst++ = *src++) != '\0);
But I'm not sure I recommend compressing it that much.
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.