I am new in C. I am referring to the book "The C Programming Language" by Brian W Kernighian and Dennis Ritchie.
There is a code for pointer increment and assignment given in the book as follows.
#include<stdio.h>
int main()
{
char *s = "Goal";
char *t = "Home";
while(*s++ = *t++) printf(*s);
return 0;
}
The code is saved and compiled using the command
gcc ptr.c -o ptr -std=c99
Now on running the code by running command
./ptr
I get the following error
Segmentation fault (core dumped)
The error seems to be inside the while loop condition.
But the code is exactly as given in the book.
What am I missing?
s and t are both string literals, and you can't modify a string literal. But this piece of code
*s++ = *t++
will modify s, which causes segmentation fault.
To fix it, use a char array. I also modified the printf part to make it legal.
#include<stdio.h>
int main()
{
char arr[] = "Goal";
char *s = arr;
char *t = "Home";
while(*s++ = *t++)
;
printf("%s\n", arr);
return 0;
}
However, I think this program is better done using an individual function to copy the string, the program will look clearer.
#include<stdio.h>
void my_strcpy(char *s, char *t);
int main()
{
char s[] = "Goal";
char *t = "Home";
my_strcpy(s, t);
printf("%s\n", s);
return 0;
}
void my_strcpy(char *s, char *t)
{
while(*s++ = *t++)
;
}
The problem is that printf expects the first parameter to be a char *, that is, something that points to a character, or the address of a character. When you say printf(*s) you're passing it an actual character, i.e. a number from 0 to 255 or -128 to 127, and the program is going to treat that number as an address, which isn't going to be a valid address on your system.
When ever we say *s = "hello" , s is pointing to an address which is present in text segment("hello" goes into text segment). So obviously changing the value of text segment results in SEGV termination.
And for s[] = "hello" if we do *s++, we are incrementing (modifying) the base address so we got "lvalue required as increment operand" error.
Related
I've been trying to change upper case letters to lower case letter using pointers but I keep getting segmentation faults. Here is my source code:
#include <stdlib.h>
#include <string.h>
char *changeL(char *s);
char *changeL(char *s)
{
char *upper = s;
for (int i = 0; upper[i] != '\0'; i++)
{
if (upper[i] >= 'A' && upper[i] <= 'Z')
{
upper[i] += 32;
}
}
printf("%s\n", upper);
return upper;
}
int main()
{
char *first;
char *second;
first = "HELLO My Name is LoL";
printf("%s\n", first);
second = changeL(first);
printf("There is no error here\n\n");
printf("%s\n", second);
return 0;
}
Using gdb I found the seg fault to be in "upper[i] += 32;". I don't understand why the seg fault is there.
"HELLO My Name is LoL" is the constant memory. You can`t change it. However you pass pointer to this memory(first) to a function which tries to change it. Thus you got segmentation fault. You should copy this string to memory butffer. Like
char buffer[] = "HELLO My Name is LoL";
and then pass buffer to changeL
A couple of notes in addition to what #Alex correctly points out in his answer. First
char *changeL(char *s);
char *changeL(char *s)
{
....
}
There is no need for a prototype before the function if the function is one line below. A prototype is used to inform code below it that the function described by the prototype exists and is defined elsewhere. If you define the function immediately below the prototype it makes the prototype irrelevant.
Second as noted in Alex's answer, on a overwhelming majority of systems, a String Literal, e.g. the "Something Here" in char *s = "Something Here"; is immutable and resides in read-only memory and any attempt to modify the string literal generally results in a SegFault.
Instead you need to create an array of characters which can be modified, e.g.
char first[] = "HELLO My Name is LoL";
or with C99+ you can use a Compound Literal to initialize first as a pointer to an array of char, e.g.
char *first = (char[]){ "HELLO My Name is LoL" };
In both cases above the characters pointed to by first will be modifiable.
Addition Per Comment
"can you also explain to him why is he getting segfault at upper[i] += 32;"
Yes. At mentioned above, when you initialize a pointer to a String Literal on virtually every current system (ancient systems had no distinction or protection for read-only memory -- all memory was writable). In the current day, creating a string literal (e.g. "foo") creates the string in memory which cannot be modified. (for ELF executables, that is generally in the .rodata section of the executable -- dissecting closer ".ro...data" meaning "read-only data")
When any attempt is made to change data that cannot be modified, a Segmentation Fault generally results because you have attempted to write to an address within a segment that is read-only. (thus the Segmentation Fault -- of SegFault)
In the code above as originally written with
first = "HELLO My Name is LoL";
If you compile to assembly (on Linux, e.g. gcc -S -masm=intel -o mysaved.asm myfile.c you will see that the string "HELLO My Name is LoL" is in fact created in the .rodata section. You do not have any ability to change that data -- you now know what happens when you try :)
The code as written in the Question also shows confusion about what the pointers first and second actually point to. By assigning the return of changeL to second, there is no new memory created for second. It is no different than simply assigning second = first; in main(). second is just a separate pointer that points to the same memory referenced by first. A more concise version of the code would be:
#include <stdio.h>
void changeL (char *s)
{
for (int i = 0; s[i]; i++)
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] += 32;
}
int main (void)
{
char first[] = "HELLO My Name is LoL";
char *second = first;
printf("%s\n", first);
changeL(first);
printf("%s\n", second);
return 0;
}
(note: both header files in the original code are unnecessary, <stdio.h> is the only required header)
To illustrate second simply points to first:
Example Use/Output
$./bin/chars
HELLO My Name is LoL
hello my name is lol
This code outputs inly the lower case in string
#include<stdio.h>
#include<string.h>
int main()
{
char b[50];
printf("String=");
scanf("%[a-z]",b);
printf("%s",b);
return 0;
}
It is a code written to copy one pointer to another.
ERROR is Segmentation error (core Dumped)
#include<stdio.h>
char strcp(char *,char *);
int main()
{
char *p="string",*q;
printf("%s",p);
strcp(p,q);
printf("%s",q);
return 0;
}
char strcp(char *p,char *q)
{
int i;
for(i=0;*(p+i)!='\0';i++)
*(p+i)=*(q+i);
}
char *p="string"...
strcp(p,q);
What p points to is a literal and literals are read-only. Trying to copy anything to it is forbidden (and causes a segmentation fault).
...and q is not initialized, another possible cause of the seg fault.
The problem with this algorithm is an implicit assumption it makes about pointers: char *q is not a string, it is a pointer to character. It can be treated as a string if you allocate space and place a null-terminated sequence of characters into it, but your code does not do it.
You can allocate space to q with malloc, like this:
char *p="string";
char *q=malloc(strlen(p)+1);
In addition, your version of strcpy reads null terminator from a wrong pointer, and does not null-terminate the copied string:
char strcp(char *p, char *q)
{
int i;
for(i=0;*(q+i)!='\0';i++) // <<== Fix this
*(p+i)=*(q+i);
*(p+i) = '\0'; // <<== Add this line
}
As the other answers have indicated the problem starts* with char *p="string",*q;.
The Literal "string" compiles to the equivalent:
const char foo[7] = {'s','t','r','i','n','g','\0'}; why *\0
As you might see further in you code you're trying to copy data to a const a\rray. Which is illegal.
But you're playing C, you have implicitly casted the const char foo[] to a char *p, right there during initialization.
C is not type safe because it's tightly coupled to the actual instruction on the hardware. Where types don't exist anymore, just widths. But that is for another topic.
*It's not the only flaw. I tossed in a few explanatory wiki links. Because the question shows you're a novice programmer. Keep up the work.
I was working on the "c programming language" exercise2.4, which deletes each character in s1 that matches any character in the string s2.Below is my code, it works. However,if I change the definition in Main function from char c1[ ]="abcde" to char *c1="abcde", it will turns out segmentation fault(core dumped). Any idea why?
#include<stdio.h>
#define UNREPEAT 1
#define REPEAT 0
void squeeze(char *s1,char *s2);
void main(){
char c1[]="abcde";
char c2[]="cd";
printf("c1 is %s\n",c1);
squeeze(c1,c2);
printf("the final result is %s\n",c1);
}
void squeeze(char *s1,char *s2){
int i,j,judge;
int k=0;
for(i=0;*(s1+i)!='\0';i++){
judge=UNREPEAT;
for(j=0;*(s2+j)!='\0';j++){
if (*(s1+i)==*(s2+j)){
judge=REPEAT;
break;}
}
if( judge==UNREPEAT){
* (s1+k)=*(s1+i);
k++;}
}
*(s1+k)='\0';
}
Because
char c1[] = "abcde";
declares an array, it's readable and writable and will always have 6 bytes (if you consider the terminating '\0') you can't extend it but of course you can use it to store less bytes.
While
char *c1 = "abcde";
is a static string literal it shall not be modified, you should always declare string literals using the const qualifier, that way the compiler would warn you if you try to modify its contents, which is illegal, just use
const char *c1 = "abcde";
char *c1="abcde"
This is a string literal which is read-only and in your function you try to change the contents of it so you might hit seg fault.
Whereas the other case
char c1[] = "abcde";
You are allocating memory on the stack whose contents can be modified.
PS: String literals are read-only
Hi in my below code for testing realloc() I am getting the error Aborted (Core dumped).
The output of the code is below
$ ./realloc
My Name // <<-- This works
Aborted (core dumped) // <<-- why this error occur?
calling to copyThis() function 1st time gives the correct result and produce no error. In the same way if I call copyThis() for the second time, it make error. I can not understand why is it happening. Can any body point me out where the problem is and what the tweak should I do?
The code is below
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *copyThis(char *str1, const char *str2, size_t size);
int main(void)
{
char *ptr1 = "My Name ";
char *ptr2 = "is Alice";
char *ptr3;
char *ptr4;
ptr3 = copyThis(ptr3, ptr1, strlen(ptr1) + 1); // This works
printf("%s\n", ptr3);
ptr4 = copyThis(ptr4, ptr2, strlen(ptr2) +1); // this line make Aborted (core dumped)
printf("%s\n", ptr4);
}
char *copyThis(char *str1, const char *str2, size_t size)
{
str1 = (char *) realloc(str1, size);
strncat(str1, str2, strlen(str2));
str1[size] = '\0';
return str1;
}
NOTE : Please point me any good tutorial site that can help me catch string operations well in c/
The error is with:
char *ptr3;
char *ptr4;
You should initialize them to NULL:
char *ptr3 = NULL;
char *ptr4 = NULL;
Since ptr3 and ptr4 are not NULL, realloc assumes they were pointers to valid memory. From man realloc:
Unless ptr is NULL, it must have been returned by an earlier call
to malloc(), calloc() or realloc().
Since those pointers are, instead, pointing to random addresses in memory, realloc will get confused when actually using them.
As a more general note, in cases like this it is very helpful to enable additional diagnostics when compiling your code. For example, if you use gcc and if you compile your code with -Wall, the following warnings are emitted:
file.c:19:1: warning: control reaches end of non-void function [-Wreturn-type]
file.c:14:10: warning: ‘ptr3’ is used uninitialized in this function [-Wuninitialized]
file.c:17:10: warning: ‘ptr4’ is used uninitialized in this function [-Wuninitialized]
How to enable those extra warnings depends on what IDE you use for developing, if any; otherwise, if you use the commandline, just add -Wall to the call to gcc.
str1[size] = '\0';
writes beyond the allocated memory, thus your program crashes. You should allocate one more for the 0 character.
The other error is that you call your function fro char* as first argument (ptr3 and ptr4) that are not initialized. This has undefined behavior, anything can happen with such code.
A proper way would be to check if that argument is 0 and then just call malloc with the appropriate size. But for such an approach to work you would have to properly initialize these variables with 0, which you should do, anyhow.
Also, don't cast the return of malloc and relatives, this might hide subtle bugs.
The problem with your code is that you have not initialized the pointers and when you are trying to use realloc it has garbage value and gdb shows :
Breakpoint 1, copyThis (str1=0x7fffffffe060 "\001", str2=0x4007e4 "My Name", size=8) at prog1.c:22
22 str1 = (char *) realloc(str1,size);
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a95b54 in realloc () from /lib/x86_64-linux-gnu/libc.so.6
Due to the garbage address it was failing. So whenever you use pointers always try to initialize them before using.
So the correct program is :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *copyThis(char *str1, const char *str2, size_t size);
int main(void)
{
char *ptr1 = "My Name";
char *ptr2 = "is Alice";
char *ptr3 = NULL;
char *ptr4 = NULL;
ptr3 = copyThis(ptr3, ptr1, sizeof(ptr1) ); // This works
printf("%s\n", ptr3);
ptr4 = copyThis(ptr4, ptr2, sizeof(ptr2) ); // this also works
printf("%s\n", ptr4);
}
char *copyThis(char *str1, const char *str2, size_t size)
{
str1 = (char *) realloc(str1,size);
strncat(str1, str2, strlen(str2));
str1[size] = '\0';
return str1;
}
I am trying to understand pointers and here is a program in K&R that I am trying to implement.
The program is strcpy with code from KR.
/*strcpy: copy t to s; pointer version 3*/
void strcpy(char *s, char *t){
while(*s++ = *t++)
;
}
So to implement this program, I add
#include<stdio.h>
int main(){
char *s="abc", *t="ABC" ;
strcpy(s,t);
printf("%s\n",t);
printf("%s\n", s);
return 0;
}
However I am getting segmentation error when I run it. I am sure I am missing something but not quite sure what.
Thanks!
char *s="abc", *t="ABC" ;
string literals are not modifiable, however, a char array can be modified, so change it to :
char s[] ="abc", *t="ABC" ;
Literal string values are stored in a read-only memory page; they cannot be modified.