I have a small conceptual question about pointers. This may be embarrassing but I need to know the answer.
I was trying to read a line from a file using getline function. getline takes char ** as its first argument and that is where the line pointer is stored. Please see the pasted code below and tell me the difference between them. Notice the declaration and use of readLine pointer.
The second code gave me segmentation fault when it reached printf(). I checked the value at *readLine with gdb(before printf()) and it was correct, but when it goes to printf(), boom SIGSEGV
this code works:
FILE *fp;
char *readLine;
readLine=NULL;
int s=0;
while(getline(&readLine,(size_t *)&s,fp) != -1){
printf("%s\n",readLine);
}
this code does not work:
FILE *fp;
char **readLine;
*readLine=NULL;
int s=0;
while(getline(readLine,(size_t *)&s,fp) != -1){
printf("%s\n",*readLine);
}
cheers...
rv
(size_t *)&s
This will crash a 64-bit machine with 32-bit ints. The solution to this kind of problem is to declare the type which is required (size_t s;), not to cast anything. In x86-64, this assigns 8 bytes to a 4-byte location on the stack, which results in stack corruption. Because the overwrite happens in the called function, it could overwrite the return address, for example.
char **readLine;
*readLine=NULL;
This is also an instant crash. You are assigning a value to the target of an uninitialized pointer, changing the bytes at some unknown point in memory.
In the first case, the variable readLine -- the value of which is stored on the stack, in it's own special reserved area -- is a pointer-to-char. When you pass its address to getline(), you tell getline() to store the real pointer into the memory which is reserved for it. Everything works.
In the second case, readLine is a pointer-to-pointer-to-char, and again, there is space reserved for it on the stack. When you call getline(), though, you're telling getline() that the readLine variable holds the address in which the pointer-to-char should be stored. But readLine points to some random memory somewhere, not to a location which getline() should be allowed to store data. In fact, you've already started corrupting memory when you write
*readLine = NULL;
because as I said, readLine is pointing to memory you don't own. getline() just makes it worse.
Please find example bellow, it is compiled end executed on Ubuntu 18.04.
If you use linux, please type "man getline", man pages are your friend.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char *readLine;
FILE *fp;
size_t n = 0;
readLine=NULL;
fp = fopen("example.c", "r");
while(getline(&readLine,&n,fp) != EOF){
printf("%s\n",readLine);
}
free(readLine);
}
Related
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 2 years ago.
Improve this question
Can someone help me with this issue? For some reason, no matter what I try, doing printf isn't printing in my code. I've been trying to use the flush method, but that seems to just cause other issues. Is there anything else I can do?
int main(void) {
char **line;
size_t bufsize=MAXBUF;
printf("Type your name: ");
getline(&line,&bufsize,stdin);
}
You are trying to pass in an empty pointer, this pointer isn't pointing to any meaningful address at the start of the program. So the problem with this is that even if you were successful, it would overwrite some random address in memory with the input, and this is almost certainly what you don't want as it will cause a segmentation fault or a crash.
However that's not the reason why it's not compiling. It's not compiling because you are trying to pass in char *** to a function that expects char **.
So what I would do is:
#include <stdio.h>
int main(void) {
char *line=NULL;
size_t bufsize=0;
printf("Type your name: ");
if(getline(&line,&bufsize,stdin)==-1){
puts("Error: User cancelled input.");
}
else{
printf("Entered: %s",line);
}
free(line);
return 0;
}
So what this code does is that it creates a pointer to an array of characters but it sets the pointer to point to NULL so that the program can see it's not pointing anywhere, and then its address (&line) is given to the getline function, this turns the char * type into a char ** which is required by the getline function. Whenever you put a & in front of something in C, it adds an extra * to the type.
You probably know this already, but if a function states that it returns something, you should always return something, even if it's nonsense otherwise depending on the type that should be returned, that can sometimes cause a crash.
There was nothing wrong with your use of printf, that was fine. printf doesn't necessarily fflush to stdout, often the \n character will trigger a flush, but it's implementation specific, so if your program crashes before an fflush is written to stdout, then you may never see the last printf. You can try an fflush(stdout); before the getline function, but after the getline function may not work because it may crash before then.
In your case what's happening is that before the printf writes to the screen the getline takes in the address of the pointer (or pointer to a pointer), dereferences it to a pointer, isn't able to, so before you have a chance to enter in any keystrokes it crashes, losing the stdout pipe, so you end up never seeing your prompt.
When i run the altered code, above, i get:
XXXX#dell:~$ gcc Deleteme.c
XXXX#dell:~$ ./a.out
Type your name: test1
Entered: test1
XXXX#dell:~$
And it runs correctly under Linux. The key lesson in all this is that, terminals are slow to update on the screen, so some time in the past, the decision was made to separate out the functionality of printf.
There are two components to printf:
The part that writes the text to stdout.
The part that updates the terminal with stdout (the flush).
This question is interesting because 1) succeeded, but the system crashed before 2) was completed.
The documentation for getline says the first parameter must be a char **, but you pass it the address of line. Since line is a char **, &line is a char ***, which is the wrong type.
getline() allocates and re-allocates memory. The address of the length and the buffer need to reflect that.
int main(void) {
char *line = NULL;
size_t bufsize = 0;
printf("Type your name: ");
fflush(stdout); // Flush output before reading.
ssize_t len = getline(&line, &bufsize, stdin);
if (len < 0) Handle_error_with_code();
...
free(line); // When done, free memory
}
There is no need to allocate any memory before calling getline().
getline() expects line, bufsize to reflect allocated data via malloc() and friends. It is easy enough to start with NULL, 0 here.
First you are passing the address of line to the getline(), the documentation says that the first argument is the address of the first character position where the input string will be stored. It’s not the base address of the buffer, but of the first character in the buffer.
This pointer type (a pointer-pointer or the ** thing) causes massive confusion.
second your line pointer does not point to anything this should fix it:
char *line = (char *)malloc(bufsize * sizeof(char));
now line is pointing to valid memory address which we requested via malloc() function.
in linux, I am trying the below code which is causing segmentation fault error:
int main(int arg_count,char *args[]){
char *buffer;
if(arg_count>1)
buffer = args[1];
else
*buffer = 0;
}
I know that pointers point to read only part of the memory, so I changed my first try buffer[0]=0; to above. But I don't understand why this one is not working either?!
The final line of your function, *buffer = 0, is attempting to set the value referred to by the pointer buffer.
As buffer has never been initialised and therefore contains an indeterminate value, dereferencing buffer is very likely to cause a segfault.
For most projects you should never write argument parsing code yourself. There are many robust and efficient libraries that will do a much better job than you (or I) could. As you are writing C on Linux GNU getopt is a good option.
if you go through your program line by line you'll see that if the user doesn't pass any arguments then buffer is just a random value. As another comment said you need to initialize it. In your case I don't think you literally want to put the value 0 in the memory address that buffer points to. Here is code that shows how to handle arguments
int main(int argc, char **argv){
char *buffer = NULL;
if(argc > 1){
buffer = argv[1];
}
else{
buffer = malloc(1024);
puts("please enter an argument");
fgets(buffer, 1024, stdin);
//do stuff with buffer
free(buffer)
}
return 0;
}
in the code above the program checks if any arguments were passed to the program, if no arguments were passed then the program allocated 1024 bytes and points buffer to that memory location and then asks the user for input. From this point you can do what ever you want with buffer.
buffer character pointer is not initialised. Since buffer is declared with auto storage class it will have a garbage value. You are trying to access a uninitialized pointer which is a memory access exception hence it gave a seg fault. Before accessing buffer allocate a memory using calloc or malloc.
I am very new to both using Linux and creating anything remotely serious on C. I've been trying to create a program which will simply compress a single string, but I keep getting this Segmentation fault when trying to run the compiled file.
I compiled it using:
gcc 2.c -o test.o -lz
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <assert.h>
int main ()
{
char *istream = malloc(10), *ostream = malloc(120);
istream = "istream";
int res = compress(ostream, (uLongf *)strlen(ostream), istream,(ulong)strlen(istream));
return 0;
}
Could anyone explain to me why this error is happening and how can I improve my code?
This line would appear to be the main problem:
(uLongf*)strlen(ostream)
You are interpreting a size_t value as a pointer to a buffer. You are meant to pass the address of an unsigned long containing the length of the output buffer. Take another look at the documentation for compress.
On top of that you don't yet understand how C strings work. The assignment operator when used with a char* lvalue merely copies an address and not the contents of a string. I suggest that you declare your buffers like this:
const char *istream = "istream";
char ostream[120];
I think your program should be something along these lines:
int main(void)
{
const char *istream = "istream";
char ostream[120];
uLongf destLen = sizeof(ostream);
int res = compress((Bytef*)ostream, &destLen, (Bytef*)istream, strlen(istream));
return 0;
}
Note that I wrote the code assuming that you are using a C compiler. And hence int main(void).
First you make istream point to memory you allocate:
char *istream = malloc(10)
then you make it point to a literal (and therefore constant and read-only) string:
istream = "istream";
You need to copy the string literal into the allocated memory, or you will no longer have the original pointer you allocated and have a memory leak. You also will not be able to free that pointer, since istream points to something you haven't allocated with malloc.
As for the crash, see the answer by David Heffernan.
As a side-note, there is no C++ in your code, only pure and plain C.
Change:
istream = "istream"
To
strcpy(istream,"istream");
In addition, what did you expect strlen(ostream) to return? 120?
strlen returns the index of the first 0 character encountered within the input string.
In your case, the contents of the memory pointed by ostream is unknown (i.e. "junk").
strlen will scan this memory until a 0 character is encountered, but will probably exceed the 120-byte memory space and cause a memory access violation.
Change strlen(ostream) to 120 if that was your intention.
I want your help in something that should be easy and I do not know why it does not work. I want to read the first data from a bin which I know that it is an int. I am using the following part of code, but I am getting a segmentation fault:
int main(int argc, char **argv)
{
int *data;
/*opening file*/
FILE *ptr_myfile;
ptr_myfile=fopen(myfile.bin","rb");
if (!ptr_myfile)
{
printf("Unable to open file!\n");
return 1;
}
/*input file opened*/
printf("I will read the first 32-bit number\n");
/*will read the first 32 bit number*/
fread(&data,sizeof(int),1, ptr_myfile);
printf("Data read is: %d\n",*data);
fclose(ptr_myfile);
return 0;
}
I also tried calling it like this:
fread(data,sizeof(int),1, ptr_myfile);
It should be something with the pointer but I cannot see what.
Change:
int *data; to int data;
printf("Data read is: %d\n",*data); to printf("Data read is: %d\n",data);
You are reading the int into a pointer, then trying to dereference the pointer (which is has a value that's meaningless as a pointer).
You don't have any memory allocated to data and in the first example code you are not using the pointer correctly. This is an alternative that could work:
int data;
and then you would use it like so:
fread(&data,sizeof(int),1, ptr_myfile);
In your original code, here you are writing an int into a pointer:
fread(&data,sizeof(int),1, ptr_myfile) ;
and then this *data will be dereferencing an invalid pointer. In the alternative case:
fread(data,sizeof(int),1, ptr_myfile);
you will be using a pointer that has no memory allocated to it.
You are passing address of a non-allocated pointer. Remove * from data:
int data;
...
fread(&data, sizeof(int), 1, ptr_myfile);
You are overthinking the question of pointers. The function fread() needs to know the address at which to store the data it reads, so it is passed a pointer. That does not mean that the buffer used by fread should be a pointer, just that fread needs a pointer to that buffer.
So your first error is writing int *data; where you really just mean int data;. You say the data element to be read is an int, so you would just declare an int to hold it.
You correctly called fread to read the data by passing a pointer to actual allocated and valid memory (i.e. &data), avoiding a different beginner pitfall of passing the uninitialized pointer to integer you declared earlier.
Your actual error is caused by writing *data in the call to printf. The * operator takes a pointer and dereferences it. In other words, it assumes that the pointer points to something, and retrieves that value. However, when the pointer being dereferenced does not point to any valid something, you have a problem.
And here is where you were lucky. The value you read from the file was then treated by the * operator as an address in memory, but happened to be an address that was not part of the memory accessible to your process, which caused the system to take notice and halt the process with a segmentation fault. As a result, you learned immediately that there was an error in your code.
If you were not lucky, the integer you read from the file when treated as an address of memory could have been the address of something already present in your process. You would not have caused a segmentation fault, and you would by puzzled by why printf produced a strange answer.
Now imagine if you read a value from an untrusted source and treated it as an arbitrary address. An attacker could use that error to learn something about your program. Worse, if you had written to the memory referenced by that pointer, the attacker could conceivably change the behavior of your program.
As a final point, note that reading and writing int (or any other data type) is safe and acceptable as long as that file will never be transferred to a different system. Not all systems agree on even the simplest definitions like the number of bytes occupied by an int (which you did handle correctly by using sizeof(int), but not in a way that would allow exchanging the file with a system with a different integer size), or even the order of those bytes in memory. Correctly addressing these issues is a large subject.
I wanted to know the issue in the following code. This is used to first open a file of size 800Mb to fill in the name variable and later used to access the data stored in it by reading indices from another file. The problem is that after reading and filling the array name, on accessing any element from it gives a seg fault. When the same code was tested on smaller data size, it worked. What could be the reason? The hardware on which I am running this is a 4gb RAM,32 bit linux version on intel i5 chip.
#include <stdio.h>
#define MAXINT 61578414
int main(int argc,char** argv){
printf("Starting \n");
FILE* fp1 = fopen(argv[1],"r");
FILE* fp2 = fopen(argv[2],"r");
char** name;
name = (char**)malloc(MAXINT*sizeof(char*));
char* tname;
int i = 0;
int tmp1;
//reading to fill in name
while(i < MAXINT){
name[i] = (char*)malloc(20);
fscanf(fp1,"%d%s",&tmp1,name[i]);
i++;
}
//accessing elements from name
int i1,i2;
while(!feof(fp2)){
fscanf(fp2,"%d%d",&i1,&i2);
fprintf(stdout,"%s %s\n",*(name+i1),*(name+i2));
}
}
There is one potential problem with your allocation of name.
char** name;
name = (char**)malloc(MAXINT*sizeof(char*));
name is defined to be a pointer to pointer, which is an array of pointers. The code is allocating MAXINT number of pointers, which is a very large number. You need to check if next was successfully allocated or a NULL pointer was removed i.e. whether malloc was successful or not.
Similarly, name[i] = malloc(20) should also be checked for NULL pointer as your system may potentially run out of memory.
Is this on Linux? In that case, (by default) non-NULL values from malloc doesn't tell you if you actually have access to the amount of memory you request. In practice, this means you can malloc far more memory than you have available. It's only when you actually access it, that the memory it going to be allocated.
So write a simple loop which just reads or writes to each byte you malloc'ed and see if that crashes. If it does, than well that's your problem.
As a side-note, have you considered using mmap() instead to deal with large file/memory allocation?
I imagine that name = (char**)malloc(MAXINT*sizeof(char*)); actually fails, you should check for a NULL return value there.