I have a problem which I figure I could solve in a matter of seconds, but I can't seem to figure it out.
What I'm trying to achieve is to create a configuration for my program and save all settings at once in my file without too much overhead.
In the past I could manually create the structure using snprintf to pass the integer into a char* which was previously allocated by memory and directly write that to file, but now instead of that, I would rather use a flat-out structure so that if I want to modify values directly, I can instead of using snprintf and atoi functions to convert strings to numbers and vice-versa.
I use the apr_file functions to open the file as I am writing an apache module.
This is my attempt that failed:
typedef struct{
unsigned int setting1;
unsigned int setting2;
} allsettings;
void read_a_file(request_rec *r){
allsettings *arecord; //compiler didn't complain at this line but might have caused segfault
apr_file_t *f;
const int perm=0x0755,ior=APR_FOPEN_READ|0,iow=APR_FOPEN_WRITE|APR_FOPEN_CREATE|0;
if (apr_file_open(&f,strres,ior,perm,r->pool)==APR_SUCCESS){
rv=apr_file_gets(arecord,sizeof(arecord),f);
rv=apr_file_close(f);
}
}
Upon compiling, I received the following error which I'm trying to work out:
warning: passing argument 1 of 'apr_file_gets' from incompatible pointer type
However, I got rid of that warning by casting argument 1 with char*.
Is there an easy way I could just load the file into two local integers to the function without having to do any string conversion or allocating extra memory into the function and without receiving a segmentation fault?
Firt of all, arp_file_gets() gets a string from a file. I believe you have to parse what you want from the string you got out. There are also somethings you need to care in the posted code segment:
1. arecord pointer is not allocated hence the pointer you passed into arp_file_gets is an invalid pointer.
2. In this call: rv=apr_file_gets(arecord,sizeof(arecord),f); I understood that you need to read out 2 unsigned int from the file f. But sizeof(arecord) = 4 (bytes) as a size of a pointer (not a struct size).
apr_file_gets "read a line from the specified file" (Apache Portable Runtime - File I/O Handling Functions.
Upon compiling, you receives the warning because the implicit cast from type struct* to type char* is not allowed (or allow just you get a warning). If you want to do that you have to cast it explicitly:
rv=apr_file_gets((char*)arecord,sizeof(arecord),f);
But, the problem with your code are the followings:
1.) The memory allocation for the record pointer missed. A possible solution is the following:
allsettings arecord;
...
rv=apr_file_gets((char*)&arecord,sizeof(arecord),f);
2.) But the main problem is that you try to use a file reader method created for character/string reading for binary reading. For example, if in your file is a "1234" string than with apr_file_gets method you will get an integer with value 0x34333231 = 875770417, not 1234. To avoid that problem you have to implement a parsing method.
Related
I have two files, User.h and test.c:
User.h
include <stdio.h>
#include <string.h>
struct User {
char name[21];
};
struct User newUser(char* name) {
struct User newUser;
memset(newUser.name, '\0', 21); // ensure string ends with '\0'
memcpy(newUser.name, name, 20); // copy first 20 chars of string
return newUser;
}
test.c
#include "User.h"
int main() {
struct User testUser = newUser(34);
printf("name is: %s\n", testUser.name);
return 0;
}
I am intentionally passing the wrong type of argument to a function, but trying to avoid a segfault:
If I were to pass a string of any length to newUser(), this function would take up to 20 characters of it and store it in .name without a segfault because it is guaranteed to end with a null byte.
Here I am passing an int instead. Because it is expecting a string, I get a compiler warning, but it compiles anyway. When run, I get a segmentation fault.
I suppose the segmentation fault occurs when the function reads past the name[21] array, but if it's guaranteed to have a null byte, why does it continue to read past it? It's expecting a string, shouldn't it treat any argument like a string and terminate at '\0'?
It seems my logic is flawed, can someone educate me about what's really going on here?
I am intentionally passing the wrong type of argument to a function, but trying to avoid a segfault.
That is same as saying I'm going into the sea but trying to avoid getting wet.
When you do something illegal, all you can end up with is invoking undefined behavior which may lead to segfault.
The best way to avoid it is to write correct code.
The problem is, the function expects a char* and you're passing an int. That's not allowed, anyway. It's wrong, and you must not ignore compiler warnings.
To elaborate, the function expects a pointer-to-char (char*) type and further, the code involves reading from the address location pointed by the pointer. As you're passing an int to the function (ignoring compiler warnings), the code tries to access the memory pointed by the supplied integer value, which is very likely an invalid memory location. So, this attempt to access invalid memory location invokes the UB.
The standard C answer is that this code has undefined behavior so all bets are off. (Pass -Werror to treat all compiler warnings as errors and pass -pedantic to get all diagnostics required by the standard.) As pointed out by Keith Thompson, the C standard actually requires a diagnostic message for this (broken) code, and a compiler may refuse to compile it.
In practice the code probably reinterprets the number 34 as a memory address and then memcpy tries to read from (char *)34. This normally causes a segmentation fault because that address falls within the first page of memory, which isn't mapped to detect when someone dereferences a null pointer.
struct User newUser(char* name)
...
struct User testUser = newUser(34);
All the relevant declarations are visible at the point of the incorrect call. Your newUser function requires an argument of type char*, and you're passing it an int. This is a constraint violation, which means that any conforming compiler is required to issue a diagnostic message.
Unfortunately (but legally), some compilers will issue a non-fatal warning for this particular error, at least in their default mode.
The solution is to invoke your compiler in a mode that treats this as a fatal error. For example, if you're using gcc or clang, you can add one or more command-line options, such as -Werror or -pedantic-errors.
If you choose to ignore any errors (which you really shouldn't do), there is no good way for your program to avoid the problem. The only solution is to avoid writing the invalid call in the first place (and use your compiler's diagnostic to help you do that).
Having said that, there are some unrelated problems in your code.
struct User newUser(char* name)
Since the function doesn't modify the data that name points to, the parameter should be defined as const char *name.
memset(newUser.name, '\0', 21);
21 is a magic number. There's nothing to tell the reader why 21 is the correct number of bytes, and if you later decide to change the length of name, you'll have to manually update all references to it. Define a constant and use it.
memcpy(newUser.name, name, 20);
What if the string is less than 20 characters long? You want to copy no more than 21 bytes and no more than the length of the argument into newUser.name. (Doing this is more complicated than it should be strncpy is the obvious solution, but it's also almost certainly the wrong solution. See [my rant on the topic]https://the-flat-trantor-society.blogspot.com/2012/03/no-strncpy-is-not-safer-strcpy.html)).
You have a function definition in a header file. Don't do that. Functions should be declared in .h files and defined in .c files. You can get away with defining a function in a .h file if that header is included only once in your entire program. For larger programs, that's not going to be the case. (There's a lot to say about how to structure multi-file C programs, but that's beyond the scope of this answer.)
I'm stuck for sometime in this code:
#include <stdio.h>
const char *fn; int a;
int exists(const char *fname)
{
FILE *file;
if (file = fopen(fname, "r"))
{
fclose(file);
return 1;
}
return 0;
}
main(){
printf("Name:\n");
scanf("%s",&fn);
a=exists(&fn)
if(a==0){
fopen(&fn,"w");
fprintf(fn,"banhdhsjha");
}
}
When I try to run the program, it works until fprintf(fn,"banhdhsjha");, but it crashes here (Windows gives me an error) and the compiler (CodeBlocks) gives me the following notice:
passing argument 1 of 'fopen' from incompatible pointer type.
I am trying to make fprintf write data in the file, but I don't know how to do it. Can you help me?
There are quite a few problems in your code.
You are passing &fn to scanf. This &fn is a pointer to pointer of const char ** type. This does not make sense. Format specifier %s requires a const char * argument, not const char **. You are basically using the pointer fn itself as a target buffer for file name (4 or 8 bytes long, depending on your platform's pointer size). Most likely you type in longer file name than that "buffer" can accommodate. That overrides memory in your program and leads to unpredictable (undefined) behavior.
The proper form is probably scanf("%s", fn), but the problem is that you never allocated memory for the target buffer. You have to make sure that fn points to a char buffer of sufficient size to hold your file name.
Your fopen(&fn,"w") suffers from the same problem (and that is what the compiler is telling you). fopen expects an argument of const char * type and you are passing a const char ** instead. The proper form is fopen(fn,"w"), but again, see 2. Also, fopen return a file handle that you are supposed to store and use later. You are ignoring (discarding) the return value of fopen. That also makes no sense. You need an additional FILE * variable to store the return of fopen. You already know that, judging by what you did in exists function, but somehow you are ignoring that knowledge in your main.
Your fprintf call also makes no sense at all. fprintf requires file handle (of FILE * type) as its first argument. Instead, you are trying to pass it a file name. That's not going to work and that's also going to trigger a diagnostic message from the compiler. You are supposed to store the return value of fopen (as I said in 3) and pass it to fprintf.
Your exists(&fn) call suffers from the same problem as 1 and 3 and produces the same diagnostic message as your fopen call.
Stop trying to write random code. Where did you get the idea to pass file name to fprintf instead of file handle? Read the documentation for each function you are trying to use and act accordingly.
I quiet do not understand why I get error "Stack around the variable "tmp" was corrupted".
I use same code in different functions and it works well but now when function is "returning" it throws error mentioned above.
struct frame {
uint8_t dst[6];
uint8_t src[6];
};
//fill frame.dst || src exactly same way as code below without any errors or warnings
bool fcn() {
uint8_t tmp[6];
sscanf_s("00-00-00-00-00-00", "%x-%x-%x-%x-%x-%x", &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5]);
//here I compare tmp[0] == frame.mac[0]...
return true;
} //here pops the error while debugging
I use exactly same code in different part of the program but no error what so ever.
Referring C-Standard confirming systems:
To scan in an 8bit value on a 32bit machine, one needs to use the "hh" length modifier. The half of the half of 32 is 8.
For VC one needs to use a workaround scanning into unsigned ints.
The %x specifier writes to an int parameter, but you are passing pointers to uint8_t instead. Since int is a larger type on your platform, you are overwriting memory when those fields are written out. Pass in pointers to int instead and convert them to your required type as appropriate.
You will want to do this everywhere else you use this code too!
I have declared a variable as
const char* mode;
I want to get the value of mode from user. When I used
scanf("%s",mode)
I get a segmentation fault. Any suggestions?
You should have gotten a warning on the scanf call.
By declaring
const char* mode;
you make mode a pointer to const char, which means you can't modify the data that mode points to.
Then you call:
scanf("%s",mode);
which attempts to modify the data that mode points to.
gcc warns about this:
warning: writing into constant object (argument 2) [-Wformat]
So mode needs to be a pointer to non-const char. And, as others have mentioned, it needs to point to some actual data so that scanf can modify that data. That's probably what's causing your segmentation fault, but since you haven't shown us how mode is initialized, it's difficult to tell.
mode will normally point to the first character of an array. So how big does that array have to be? Since you're using scanf with a "%s" format, it can't possibly be big enough. scanf("%s", mode) reads a whitespace-delimited string from stdin into the buffer pointed to by mode. You can't avoid input that will overflow the buffer.
(If this is a beginner's exercise, you can probably ignore the buffer overflow issue for now; just make mode point to a buffer that's reasonably big, and avoid providing very long input.)
You need to allocate memory to the pointer to be able to do something meaningful with it. Declare it as:
#define MAX_LENGTH 256
char *mode = malloc(MAX_LENGTH);
Also, it is better to have stack allocation, if the memory needed is not too big:
simply,
char mode[MAX_LENGTH];
A segmentation fault is generally an attempt to access memory that the CPU cannot physically address. That is because u have not allocated memory for your variable. Note:But some c compiler like Turbo C++ allows you to do so without even allocating memory.
SOLVED See bottom of question for solution.
I'm having trouble with passing on a String argument to my function, and am getting a segmentation fault when the function is called. The program takes in a command line input and passes on the file provided to the function after validation.
My function code goes like this:
char *inputFile; //
inputFile= argv[2];
strcpy(inputFile, argv[2]);
compress(inputFile){
//file open and creation work bug-free
//compression action to be written
void compress(char inputFile){
//compression code here
}
When the function is called, a segfault is thrown, and the value of inputFile is 0x00000000, when prior to the function call, it had a memory location and value of the test file path.
Some of the variations I've tried, with matching function prototypes:
compress(char *inputFile)
compress (char inputFile[])
I also changed the variable.
Why is a variable with a valid memory address and value in the debugger suddenly erase when used as a parameter?
Edit 1:
Incorporating suggestions here, I removed the inputFile= argv[2] line, and the debugger shows the strcpy function working.
However, I've tried both compress(char *inputFile) per Edwin Buck and compress(argv[2]) per unwind, and both changes still result in Cannot access memory at address 0xBEB9C74C
The strange thing is my file validation function checkFile(char inputFile[]) works with the inputFile value, but when I pass that same parameter to the compress(char inputFile[]) function, I get the segfault.
Edit 2- SOLVED
You know something is going on when you stump your professor for 45 min. It turns out I had declared the file read buffer as a 5MB long array inside the compress() method, which in turn maxed out the stack frame. Changing the buffer declaration to a global variable did the trick, and the code executes.
Thanks for the help!
You shouldn't be writing into memory used to hold argv[2].
You don't seem to quite understand how strings are represented; you're copying both the pointer (with the assignment) and the actual characters (with the strcpy()).
You should just do compress(argv[2]); once you've verified that that argument is valid.
First, to copy something from argv[2] to somewhere else, you need some memory for "that somewhere else". You could allocate the memory based on the size of argv[2] but for our simple example, a very large fixed size buffer will do.
char inputfile[2048];
It looks like you tried to do this by the assignment operator, which doesn't really do what you intended.
// this is not the way to what you seek, as it doesn't create any new memory for inputfile
char* inputfile = argv[2];
in passing the inputfile variable to a procedure, you want to pass much more than a single character, so void compress(char inputfile) is not an option. That leaves
compress(char *inputFile) // I prefer this one
compress (char inputFile[])
which both work, but in my experience the first is preferred, as some older compilers tend to make distinctions between pointer and array semantics. These compilers have no issues casting an array to a pointer (which is required as part of the C language specification); however, casting a pointer to an array gets a bit messy in such systems.
You've not allocated any memory for the char * to use. All you've done with char *inputfile is allocated a pointer.