What line is at fault -- segfault, that is...? - c

I'm relatively new to C (and completely new to StackOverflow - hey guys!), and this segfault has been giving me no surcease of sorrow for the past few hours (DevC++ on a windows machine). It's just a simple palindrome prime program, but it's really giving me a hard time. I'm not generally a novice programmer like it seems here, but... Good god. Now I remember why I wanted to get away from C++ and to Python so quickly.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
FILE *outputFile;
char buffer[81];
char* strrev();
int bytesWritten;
char* strI = 0;
char *strrev(char str[])
{
char *p1 =NULL;
char *p2 =NULL;
if (! str || ! *str)
return str;
for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
{
*p1 ^= *p2;
*p2 ^= *p1;
*p1 ^= *p2;
}
return str;
}
main()
{
int isPrime(int);
int i,j;
outputFile = fopen("DD:OUTPUT", "w");
if (outputFile == NULL)
{
printf("open error: %d/%s\n", errno, strerror(errno));
exit(99);
}
for (i=1; i<15000; i++)
{
if (isPrime(i)==1)
{
bytesWritten = sprintf(buffer,"%d is primepal!\n",i);
fwrite(buffer, 1, bytesWritten, outputFile);
}
}
fclose(outputFile);
return 0;
}
int isPrime(int myInt)
{
int loop;
for (loop = 2; loop < myInt/2+1; loop++)
sprintf(strI, "%s%d", 10, myInt);
{
if (myInt%loop==0 && (atoi(strrev(strI))-myInt)==0)
{
return 0;
}
return 1;
}
}
I apologize ahead of time if this is a dumb question, and the answer is very obvious -- but I've officially hit the limit where no matter how logical an answer, I've been coding the same problem for too long for it to make any sense. And also, segfaults are horrible beasts. Thank you ahead of time for anything you have to offer!
~ Jordan

The line sprintf(strI, "%s%d", 10, myInt); is likely crashing.
You have not allocated any space for strI, it's defined as char* strI = 0; Make it a char[64] , or a suitable size.
You're giving the wrong arguments to sprintf, "%s%d" says the first parameter should be a string ("%s") , but you give it an int. Change %s to %d
Some other issues:
Don't use *p1 ^= *p2; hack to to swap variables, there's many cases where this does not work. Do it properly with a temp variable.
main() calls isPrime(), but there's no prototype for isPrime at that time. Place int isPrime(int myInt); somewhere before main().
The prototype for your strrev function should be char *strrev(char str[]); and not char *strrev()

Segfaults don't have to be as bad as you're experiencing. Compile the program with debugging symbols (add -g to gcc) and run it in gdb. After the segfault, type bt in gdb and press enter. It will tell you the exact line of your segfault.

for (loop = 2; loop < myInt/2+1; loop++)
sprintf(strI, "%s%d", 10, myInt);
{
if (myInt%loop==0 && (atoi(strrev(strI))-myInt)==0)
You might want to double-check where you've got that brace in relation to the for. This isn't python, and indentation alone doesn't cut it.

Related

Copy a string on a structure element in C

I don't understand why my string is not copied.
The strings structures are similar to this one "KS 2H 5C JD TD"
Here is my code (the comments are all that I tried (memcpy and strcpy)):
typedef struct Hand Hand;
struct Hand {
char *cards;
double power;
};
Hand* initHand(char *set){
Hand *hand = malloc(sizeof(*hand));
if(hand == NULL)
exit(EXIT_FAILURE);
char card[5][3];
//strcpy(hand->cards,*cards);
int i=0;
char *p = strtok (set, " ");
while (p != NULL)
{
card[i] = p;
printf("%s\n",p);
p = strtok (NULL, " ");
i++;
}
hand->power=0;
}
I was willing to copy every 2 letters in an array with strtok (this is what I wanna do); then I tried to copy the whole string.
Thanks a lot. I understood my mistakes:
Mistakes:
I didn't know that strtok is a destructive function (I was trying to edit a readonly data since I was using a constant).
I didn't allocate memory for char *cards
Solution:
make a copy of the set.
allocate memory.
Here's the code that worked for me:
struct Hand {
char cards[5][3];
double power;
};
Hand* initHand(char *set){
Hand *hand = malloc(sizeof(*hand));
if(hand == NULL)
exit(EXIT_FAILURE);
char copy_set[15]="";
strcpy(copy_set,set);
char **str = (char**) malloc(5*sizeof(char*));
for(int i=0; i<3; i++){
str[i]= (char) malloc(3*sizeof(char));
}
strcpy(hand->cards,str);
int i=0;
char *p = strtok (copy_set, " ");
while (p != NULL)
{
strcpy(hand->cards[i], p);
p = strtok (NULL, " ");
i++;
}
hand->power=0;
return hand;
}
I think that this is reasonably correct code — a relatively simple modification of the code shown in revision 2 of the answer by the OP. There's always room for improvement, of course.
#include <stdlib.h>
#include <string.h>
struct Hand
{
char cards[5][3];
double power;
};
typedef struct Hand Hand;
extern Hand *initHand(const char *set);
Hand *initHand(const char *set)
{
Hand *hand = malloc(sizeof(*hand));
if (hand == NULL)
exit(EXIT_FAILURE);
char copy_set[15] = "";
strcpy(copy_set, set);
int i = 0;
char *p = strtok(copy_set, " ");
while (p != NULL)
{
strcpy(hand->cards[i++], p);
p = strtok(NULL, " ");
}
hand->power = 0;
return hand;
}
#include <stdio.h>
int main(void)
{
Hand *hand = initHand("KS 2H 5C JD TD");
for (int i = 0; i < 5; i++)
printf("Card %d: [%s]\n", i, hand->cards[i]);
printf("Power: %.2f\n", hand->power);
free(hand);
}
As I noted in a comment, the variable str isn't needed. With the revised structure, there is no need to allocate extra cards. I've marked the string input as const. Arguably, it would be better to do a dynamic memory allocation for copy_set (remembering to release the copy too), or at least check that the given data will fit in the allocated space. It would probably be better to print an error message before unilaterally exiting on a memory allocation failure. It is good to check for the failure. It is not good to exit with saying why. It would probably be better to return NULL on failure, and then let the calling code detect and handle the problem.
There's a simple test program attached to the code too.
Be cautious about using strtok(); it is dangerous to use it in library functions. You should generally use strtok_r() (POSIX) or strtok_s() (Microsoft) for preference.

strend function in C using pointers?

I have created a function for strend, which basically returns 1 if string t is present at the end of string s, however it never returns 1:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int strend(char *s, char *t) {
int p;
for (p = 0; p < strlen(s) - strlen(t); p++) {
*s++;
}
printf("%s\n%s\n", s, t);
if (s == t)
return 1;
return 0;
}
int main(void) {
int bool = strend("Hello", "ello");
printf("%i\n", bool);
return 0;
}
This gives me an output of:
ello
ello
0
So technically I should get 1. I assume the comparison using pointers is not used in this way?
You need to review your basic knowledge of C strings. There are lots of standard string functions in string.h that can help you with this test.
The basic problem is that the test s == t is valid, but you are comparing memory addresses here. You can see that is valid if you change the strings to test to
char test[] = "Hello";
int bool = strend_(test, test+1);
where test obviously is the same as your "Hello", and similarly, test+1 is the same as "ello" (try it by printing them). This correctly returns 1 with your routine.
In addition, I get two warnings:
on *s++; "warning: expression result unused [-Wunused-value]": you increment s but also ask what character is at that position through *s; and you don't use that information.
Fix by removing the * there.
on p < strlen(s) ..; "warning: comparison of integers of different signs: 'int' and 'unsigned long'", because strlen does not return a signed integer but an unsigned one (apparently, my header uses unsigned long).
Fix by declaring p as unsigned long, or even better, size_t.
Your entire routine can be condensed to a simple
int strend (char *s, char *t)
{
if (strlen(s) >= strlen(t) && !strcmp (s+strlen(s)-strlen(t),t))
return 1;
return 0;
}
It's not worth the trouble to cache the result of those four strlen calls into 2 temporary variables; a good compiler will work it out and do that for you. (A quick glance to the assembly output of the compiler I'm using – clang – shows it does, even with the default optimization settings.)
A slightly modified test, based on #M.M.'s comment:
int strend (char *s, char *t)
{
if (strlen(s) < strlen(t)) return 0;
return !strcmp (s+strlen(s)-strlen(t),t);
}
but attempting to optimize it this way is not as easy parsed as the routine above, and its assembly is ever so slightly "wordy" as well. Personally, I'd go for the more humanly readable version.
Use strcmp(3)
if (strcmp(s, t) == 0) return 1;
This actually compares the contents of the memory pointed to by s and t rather than their addresses.
Your code is broken in multiple ways:
The initial loop is a very cumbersome way to advance p by the difference of lengths if positive.
Once you have pointers at the same distance from the end of both strings, You should compare the characters with strcmp() (or memcmp() if you can first exclude the case of strlen(s) < strlen(t).
Comparing the pointers obtained after the loop will only work if t points inside the string pointed to by s, a special case that may or may not be produced by the compiler for the specific call in main: strend("Hello", "ello");.
Here is a modified version:
#include <string.h>
int strend(const char *str1, const char *str2) {
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
return len1 >= len2 && !memcmp(str1 + len1 - len2, str2, len2);
}
I corrected/modified your code, here is the code,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#pragma warning(disable:4996)
int strend(char *s, char *t)
{
int p,flag=0,count=0;//count will be the starting index for *t
p = strlen(s) - strlen(t);//this will be the starting index for *s
while(count<strlen(t))
{
if (*(s+p) == *(t+count))
{
flag = 1;
count++;
p++;
continue;
}
else
{
flag = 0;
break;
}
}
return flag;
}
int main(void)
{
int flag = strend("Hello", "ello");
printf("%i\n", flag);
return 0;
}
This code works too.
#include <stdio.h>
#include <string.h>
int strend (char *s1, char *s2);
void main ()
{
char str1[20] = "somethings";
char str2[20] = "things";
int f;
f = strend (str1,str2);
if (f==1)
printf ("1");
else
printf ("0");
}
int strend (char *str1, char *str2)
{
int l = strlen(str1) - strlen(str2);
str1 = str1 + l;
int d = strcmp(str1,str2);
if (d == 0)
return 1;
else
return 0;
}
this code works well.
int strend(char *s, char *t){
while(*t & *s){
if(*t == *s){
t++;
}
s++;
}
return *t==*s;
}

Irregular segmentation fault

I'm attempting to run the following code:
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[]) {
/* code */
char senha [256];
if (argv[1] != NULL)
{
strcpy(senha, argv[1]);
}
char frase [256];
printf("Insira a frase: \n");
scanf("%s", frase);
int sizeS = (int)strlen(senha);
int sizeF = (int)strlen(frase);
char fraseout [sizeF+1];
int i;
int j;
for (i=0; i<=sizeF-1; i++)
{
if(j>=sizeS)
{
j=0;
}
int valF = (int)frase[i];
int valS = (int)senha[j];
valF = 32 + ((valF - 32) + (valS - 32)) % (128-32);
fraseout[i] = (char)valF;
j++;
}
fraseout[sizeF] = '\0';
printf("\"%s\" -> \"%s\"\n", frase, fraseout);
return 0;
}
When I attempt to run this on my Ubuntu 15.04 it compiles and runs perfectly. When I try to run it on a OSX Yosemite it also compiles and run flawlessly. If I, however, compile and run it on a CS50 VM although the compilation runs fine I get a Segmentation Fault error on Running. Why is this happening and why only on one singular OS?
The code works when j has an initial value of 0. This is not to be assumed on all (or any) systems, however, hence the different behavior. Initialize j.
Possible issues, with irrelevant lines removed. The uninitialized ones are likely your problem.
argv[1] != NULL is safe with C89, but is obscure and might not be on noncompliant compilers. Checking argc is more typical.
senha is left uninitialized i f the argument is not passed, which may result in buffer overflows later.
char senha [256];
if (argv[1] != NULL)
strcpy(senha, argv[1]);
Not checking the size of the buffer for input, use scanf("%255s", frase);
Not checking the return value of scanf, frase will be left uninitialized if it fails, which may result in buffer overflows later.
char frase [256];
scanf("%s", frase);
VLAs not in C89, but are in C99 and provided as an extension in GNU C89, which is the default used by gcc. This may fail to compile with strict C89 compilers.
int sizeF = (int)strlen(frase);
char fraseout [sizeF+1];
Not initialized.
int j;
This computation may yield a ", resulting in improperly quoted output.
valF = 32 + ((valF - 32) + (valS - 32)) % (128-32);
fraseout[i] = (char)valF;
printf("\"%s\" -> \"%s\"\n", frase, fraseout);

Printf and Strcpy Bug in C language

Here i wrote a piece of code. A function to add long numbers (used strings to represent numbers).
I want to know about two bugs that I usually face while coding in C
About printf statements , sometimes upon removing some printf statements i get logical errors but on putting them back the code runs perfectly. I dont understand why and do let me know how to avoid those errors too.
Eg. In below code, the line mentioned in code (comments specified too infront of that line) after commenting it back or removing it, "Answer" variable receives blank string(case 1) and uncommenting it back gives correct output(case 2)
About strcpy function, what is the bug or analogy in it that it behaves wierd sometimes
Eg. In above mentioned case 2, even though the "add" function is returning correct output why is it not getting copied correctly into the "Answer" variable.
Code is here
#include<stdio.h>
#include<string.h>
char *strrev(char *str)
{
char *p1, *p2;
if (! str || ! *str)
return str;
for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
{
*p1 ^= *p2;
*p2 ^= *p1;
*p1 ^= *p2;
}
return str;
}
char* add(char a[],char b[]){
int n1=strlen(a);
int n2=strlen(b);
char c[20],d[20];
int i,j,k=0,carry=0,sum=0;
for(i=n1-1,j=n2-1;i>=0 && j>=0;i--,j--){
sum = (a[i]-'0')+(b[j]-'0')+carry;
c[k++]=sum%10 + '0';
carry=sum/10;
}
if(i>=0){
for(;i>=0;i--){
sum = (a[i]-'0')+carry;
c[k++]=sum%10 + '0';
carry=sum/10;
}
}
else if(j>=0){
for(;j>=0;j--){
sum = (b[j]-'0')+carry;
c[k++]=sum%10 +'0';
carry=sum/10;
}
}
if(carry){
while(carry){
c[k++]=carry%10 + '0';
carry/=10;
}
}
c[k]='\0';
printf("\nResult under function = %s",strrev(c)); //upon removing this printf statement the Result variable in main() receives a blank string
return strrev(c);
}
int main(){
char answer[20];
printf("\nAnswer = %s\n",add("0","1"));
strcpy(answer,add("0","1"));
printf("\nNow Answer is %s \n",answer); // Here is the bug
return 0;
}
You have undefined behavior because you return a pointer to a local variable. The array c in the add function will go out of scope once the function returns, leaving you with a stray pointer.

free() caused segmentation fault

In this program, there is a segmentation fault. The program can print out "loop end" successfully and segmentation fault appeared after "loop end", which means there is not error in read_name function. But I could not figure out any error in my free_memory function. Could anyone help me figure out? Thank you.
input file:
9
Clinton, Hillary R.
Bonds, Bobby S.
Bonds, Barry L.
Clinton, William I.
Clinton, Chelsea T.
Bush, Laura M.
Bush, George W.
Bush, Jenna F.
Bush, Barbara G.
program:
#include <stdio.h>
#include <malloc.h>
#include<string.h>
void alloc(char ***surname, char ***first, char **mid_init, int num);
void read_names(FILE *inp, char **surname, char **first, char *mid_init, int num );
void free_memory(char **surname, char **first, char *mid_init, int num);
int main(int argc, char *argv[])
{
int num = 0;
char **surname, **first, *mid_init;
FILE *inp = fopen(argv[1], "r");
FILE *outp = fopen(argv[2], "w");
char array[79];
fscanf(inp, "%d", &num);
printf("%d\n", num);
fgets(array, 79, inp);
alloc(&surname, &first, &mid_init, num);
read_names(inp, surname, first, mid_init, num);
free_memory(surname, first, mid_init, num);
fclose(inp);
fclose(outp);
return 0;
}
void alloc(char ***surname, char ***first, char **mid_init, int num)
{
int i;
*surname = (char**)malloc(num * sizeof(char*));
*first = (char**)malloc(num * sizeof(char*));
*mid_init = (char*)malloc(num * sizeof(char));
for(i=0; i<num; i++)
{
(*surname)[i] = (char*)malloc(15*sizeof(char));
(*first)[i] = (char*)malloc(10*sizeof(char));
}
}
void read_names(FILE *inp, char **surname, char **first, char *mid_init, int num )
{
char *token, array[79];
char delim[6] = ", .\n";
int i=0;
fgets(array, 79, inp);
printf("loop begins\n");
for(i=0; i<num; i++)
{
fgets(array, 79, inp);
printf("%s", array);
token = strtok(array, delim);
strcpy( (surname[i]), token);
printf("%s ", (surname[i]));
token = strtok(NULL, delim);
strcpy( (first[i]), token);
printf("%s ", (first[i]));
token = strtok(NULL, delim);
*mid_init = token[0];
printf("%s\n", mid_init);
printf("\n\n");
}
printf("\nloop ends\n");
}
void free_memory(char **surname, char **first, char *mid_init, int num)
{
int i;
for(i=0;i<num;i++)
{
free((surname)[i]);
free((first)[i]);
}
free(surname);
free(first);
free((mid_init));
}
First off, you're limiting yourself to 14-character first names and 9-character last names, so that would be the first thing I'd check, that your names aren't longer than this.
If they are, you'll probably corrupt the memory arena when copying them.
One way to check this is to simply print the length of token every time you set it, such as:
token = strtok(array, delim);
printf ("DEBUG: token length is %d\n", strlen (token));
Keep in mind that corruption does not necessarily have a visible immediately or even ever. In this case, what's most likely happened is that you've overwritten a vital piece of inline control information in the memory arena, such as a memory block size or a pointer to another memory block.
However, there's no code actively checking for that when you write to memory so it's probably only found when you next try to do a memory allocation or de-allocation call.
Your next call like this after the corruption is your free calls and that's almost certainly where it's being found, because the arena is corrupt.
Bottom line, writing beyond the end of allocated memory is undefined behaviour. That means you shouldn't do it.
If it turns out your names aren't too long (as you state in a comment), you need to then ask yourself why you have a superfluous fgets(array, 79, inp); in your code. I understand why it's needed in main so as to move to the next line after inputting the line count with a call to fscanf. And that one does its job well.
However, you have another one at the start of read_names which effectively throws away the first name in your list. That's going to cause problems because, while your code thinks there are X names in the file, you've thrown away the first one meaning that there are only X - 1 remaining. You can tell this because, when you begin to print out the names, the first one from the file appears to be missing.
If you remove the fgets at the start of read_names, you should find it's okay.
As an aside, there's a couple of other changes I'd make to the code. First you really should check all those malloc calls in case one of them fails. That's the general rule for all functions that can fail when you rely later on them not having failed.
Second, I'm not really a big fan of ever multiplying by sizeof(char) - this is guaranteed by the standard to always be 1, so multiplying by it clogs up the code and makes it less readable.
Try replacing
token = strtok(NULL, delim);
*mid_init = token[0];
printf("%s\n", mid_init);
with
token = strtok(NULL, delim);
mid_init[i] = token[0];
printf("%c\n", mid_init[i]);
When mid_init memory chunk is filled with garbages without any null in it, 'printf("%s\n", mid_init);' might read beyond the data segment causing segmentation fault.
But #paxdiablo's answer has a much better chance to be the case here.
#Bruce, segmentation fault doesn't always appear at the exact spot it happened.
I don't know why you are getting a segmentation fault, but if I was writing this, I would try and make it a bit simpler (I don't think you are doing yourself any favors) - it makes no sense to pass around something like char ***surname.
This is only my personal opinion, but I would do something like this:
#include <stdio.h>
#include <malloc.h>
typedef struct {
char **data;
unsigned int count;
unsigned int actualSize;
} StringArray;
void StringArray_init(StringArray *array)
{
array->count = 0;
array->actualSize = 0;
}
void StringArray_add(StringArray *array, char* value)
{
if (array->actualSize == 0)
{
array->data = (char**)malloc(sizeof(char*)* 4);
array->actualSize = 4;
}
else
{
if (array->count >= array->actualSize)
{
array->actualSize *= 2;
array->data = (char**)realloc(array->data,sizeof(char*) * array->actualSize);
}
}
array->data[array->count] = value;
array->count++;
}
char* StringArray_get(StringArray *array, unsigned int position)
{
if (position < array->count)
return array->data[position];
else
return 0;
}
void StringArray_clear(StringArray *array)
{
if (array->count >0) free(array->data);
array->count = 0;
array->actualSize = 0;
}
int main ()
{
StringArray surname;
StringArray_init(&surname);
StringArray_add(&surname, "Smith");
StringArray_add(&surname, "Jones");
for(int i=0;i<surname.count;i++)
{
printf("%s\n", StringArray_get(&surname,i));
}
StringArray_clear(&surname);
}
What the above code is doing is allocating memory when you add a value, but instead of allocating space for one item, it adds enough for four. If you get to the point where you add a fifth, it will then double the space to 8 items. This method should help with memory fragmentation. I'm also using a structure, which just makes it a bit easier to pass this array around.
I could also do something like this if I wanted to allocate memory for the strings (just include the string.h header):
int main ()
{
StringArray surname;
StringArray_init(&surname);
char *name = (char*)malloc(sizeof(char) * 6);
strcpy(name,"Smith");
StringArray_add(&surname, name);
name = (char*)malloc(sizeof(char) * 6);
strcpy(name,"Jones");
StringArray_add(&surname, name);
for(int i=0;i<surname.count;i++)
{
printf("%s\n", StringArray_get(&surname,i));
}
// Free memory
for(int i=0;i<surname.count;i++)
{
char *name = StringArray_get(&surname,i);
if (name != NULL) free(name);
}
StringArray_clear(&surname);
}
The size of each name is 6 because there are 5 characters and an extra one for 0, which is the string terminator.
Sorry if this doesn't directly answer your question, but hope it helps.
Bruce,
In your data file, you need a blank line between the number and list of names because of the first fgets() at the beginning of read_names() consumes the second line.
Because the program skipped the second line, it could read only 8 names and the last line read was a blank line, which caused strtok to return a null and the next strcpy tried to read from address 0, which is, of course, a segmentation fault.
And in my machine, the fault happened before printing "loop ends".
You need to check the return values of function calls (strtok in this case) for possible errors. Otherwise you will be wasting a lot of time debugging like this.

Resources