Pass by address, scanf, de-referencing - c

I have written a program employing the following concept:
I create an integer x pass by address to a function, along with a filename, said function opens file if available, scans the first line and sets the value that pX points to equal to the scanned line.
Only it's not working, and I don't see what I'm doing wrong. As far as I can tell the code below is generally how one would accomplish it, but I'm not sure if I'm not using scanf() correctly with the pointer or what.
void foo() {
char input[20] = "test.txt";
int x = 1;
bar(input, &x);
}
void bar(char *fileName, int *pX) {
FILE *fp = fopen(fileName, "r");
char *buffer = malloc(15 * sizeof(int));
fgets(buffer, 15, fp);
scanf(buffer, "%d", *pX);
free(buffer);
fclose(fp);
}

Change line :
scanf(buffer, "%d", *pX);
to :
sscanf(buffer, "%d", pX);
You need function sscanf for what you are trying to do.
Both scanf and sscanf take a pointer as an argument. pX is of type int *, therefore a pointer to an int and should work for you. What you pass, *pX, is the contents of this pointer, in other words an int.
Also, change line :
char *buffer = malloc(15 * sizeof(int));
to :
char *buffer = malloc(15 * sizeof(char));
or simply :
char *buffer = malloc(15);
and always check the result of malloc :
if (buffer == NULL){
...
}

First of all, there's no pass-by-reference in C, function parameters are all passed by value. By passing a pointer-to-data, we make the simulation of the achieving the same effect as pass-by-reference, but that does not mean C has any pass-by-reference concept.
That said, the problem seems to be
scanf(buffer, "%d", *pX);
^^^^
where
the current syntax is invalid and invokes undefined behavior. Probably you need sscanf().
px is already a pointer to int. Passing px will be correct and suffice.
Moral of the story: enable compiler warnings and pay heed to them. They are there for a reason. With proper warnings enabled, you should see something like
warning: format %d expects argument of type int *, but argument 3 has type int [-Wformat=]
Finally,
Always check the return value of fopen() for success before using the file pointer.
Check for the return value of scanf() to ensure successful scanning.
Check for the return value of fgets() to ensure success
...basically, check return value of all library calls to make sure they worked as expected.

You use scanf() incorrectly: either use scanf directly to parse standard input or use sscanf() to parse the string read by fgets(). Furthermore, pX is already a pointer to int, which is what sscanf() expects to store the int value it converts, pass it directly: sscanf(buffer, "%d", pX);
Here is a modified version:
int bar(const char *fileName, int *pX) {
char buffer[15];
FILE *fp = fopen(fileName, "r");
int success = 0;
if (fp != NULL) {
fgets(buffer, sizeof buffer, fp);
if (sscanf(buffer, "%d", pX) == 1)
success = 1;
fclose(fp);
}
return success;
}
void foo(void) {
int x = 1;
bar("test.txt", &x);
/* do something with x */
}
Notes:
allocating buf is unnecessary, just make it a local array with automatic storage.
char *buffer = malloc(15 * sizeof(int)); is incorrect: you allocate space for 15 int instead of 15 characters, which have a size of 1 by definition. Use the size of the destination type to avoid any inconsistencies:
char *buffer = malloc(15 * sizeof(*buffer));
always check the return value of malloc() to avoid potential undefined behavior.
reading from fp without checking if fopen succeeded has potential undefined behavior.
the contents of the array pointed to by filename is not modified, make it a const char *.
it may be useful for bar to return a success indicator.
enable more warnings at compile time: gcc -Wall -Wextra -Werror or clang -Weverything -Werror might have caught the mistakes in scanf.

Related

unable to write into file

This is code to write contents in file.
#include<stdio.h>
#include<conio.h>
#include<string.h>
void main()
{
FILE *fp;
char ch(90);
fp = fopen("books.txt","w");
if(fp == NULL)
{
puts("Cannot open file");
}
printf("Enter lines of text:");
while(strlen(gets(ch)) > 0)
{
fputs(ch, fp);
}
fclose(fp);
}
I'm getting 4 errors. These are:
Cannot convert int to char * in function main().
Type mismatch in parameter __s in call to gets(char *) in function main().
Cannot convert int to const char * in function main().
Type mismatch in parameter __s in call to fputs(const char *,FILE *) in function main().
your definition of the char array is wrong I believe:
char ch(90);
must be
char ch[90];
In your code
char ch(90);
is considered as a function declaration, which is not what you want. you need to use the [] operator to denote an array, like
char ch[90]; //array named ch having 90 chars
After that, in case if(fp == NULL) is success (i.e., file opening is failed), just printing out a message is not sufficient. You should not use the returned fp further anywhere in the program, i.e., you have to skip all the statements involving that fp. Otherwise, using invalid file pointer will result in undefined behaviour.
That said,
never use gets(), use fgets() instead.
the proper signature of main() is int main(void).
Firstly:
Your character array definition should be:
char ch[90];
Secondly:
Your definition of the main should be:
int main(void)
Thirdly:
Consider using fgets() instead of gets().
Check the char array definition,
it should be like char ch[90];

C Dynamic char array

Expanding array dynamicly after user enter string in function dynamic_array My Problem seems to be when i try to use the extended array agian in main after i dynamic_array returns true.
After function call i try to print input with printf("main string: %s\n", input) the program will crash. It seems like the *input in main never gets extended.
int dynamic_array(char *input, int *string_current_len){
int string_len = 0;
char temp_c;
input = (char *) malloc(sizeof(char));
if(input == NULL) {
printf("Could not allocate memory!");
exit(1);
}
printf("File to search in: ");
while((temp_c = getchar()) != '\n') {
realloc(input, (sizeof(char)));
input[string_len++] = temp_c;
}
input[string_len] = '\0';
printf("\nYou entered the string: %s\n", input);
printf("length of string is %d.\n", string_len);
*string_current_len = string_len;
return 1;
}
int main(void) {
int string_len = 0;
char *input;
printf("enter #q as filename or word to quit.\n");
if(!dynamic_array(input, &string_len)){
return 0;
}
printf("main string: %s\n", input);
return 0;
}
This:
realloc(input, (sizeof(char)));
is wrong. The realloc() function doesn't modify the given pointer (it can't!), it returns the new pointer. It can also fail, and return NULL.
Also, the second argument doesn't make any sense at all, it should be the new desired total size of the previously allocated buffer, but you're always passing (a very obscure) 1. It's not "grow this by this amount", it's the rather more low-level "attempt to grow this to this new size, and return the new location of the grown buffer".
Please read the documentation very carefully.
realloc(input, (sizeof(char)));
You are reallocating with same size (i.e 1 byte). It shoud be:
while((temp_c = getchar()) != '\n') {
realloc(input, (string_len + 1) * (sizeof(char)));
input[string_len++] = temp_c;
if(!dynamic_array(input, &string_len)){
return 0;
}
"input" variable is used without initialization.
realloc(input, (sizeof(char)));
Above "realloc" is returning bad pointer. It may be totally bogus, or it may have been allocated from another heap. The pointer MUST come from the 'local' heap.
C has a call-by-value semantics. So any changes to formal input inside dynamic_array is not propagated to the caller (e.g. your main).
Your main does not initialize input. If you compiled with all warnings and debug info (as you should), e.g. with gcc -Wall -g, you'll get a warning about that.
I actually recommend to initialize every local variable. This makes the debugging easier (since runs are more reproductible). Useless initializations will be removed by the optimizer.
You could initialize input inside your main with
char* input = NULL;
and you should redesign your program, perhaps by having a grow_array function (instead of your dynamic_array) which you would call in your main like
grow_array (&input, &string_len);
I leave up to you to declare and implement grow_array correctly. I'm too lame to do your homework.
Inside your grow_array you want to call malloc and test it:
*pptr = malloc(newsize);
if (!*pptr) { perror ("malloc"); exit (EXIT_FAILURE); };
Don't forget to use the debugger (gdb) to run your program step by step.
I don't recommend using realloc because error handling could be tricky. I would suggest using malloc and free and cleverly copying the data using mempcy.
Read the documentation of every function that you are using, e.g. malloc(3), printf(3)
couple of things that I noticed.
int dynamic_array(char *input, int *string_current_len) should change to
int dynamic_array(char **input, int *string_current_len)
since this function is trying to modify a pointer.
also the call to the function here
if(!dynamic_array(input, &string_len)){
return 0;
}
needs to be:
if(!dynamic_array(&input, &string_len)){
return 0;
}

Most efficient way to use strcat() with a string and int?

I'm try to concatenate to char * rv with the result of a function call that will return an int. fib() returns an int. The main problem I'm running into is that strcat()'s signature requires a const char * as it's second arg:
char * strcat ( char * destination, const char * source );
Here is a small sample of my code. fib() calculates the nth fibonacci number - in this case, the 7th fibonacci number.
char * rv;
int num;
rv = (char*)malloc(2048*sizeof(char));
num = 7;
...
strcat(rv, (const char *)itoa(fib(num), rv,10));
Obviously this is wrong and won't compile. What is the cleanest way to do this? Do I need another char * var to store the results of itoa() first, instead of using rv?
Thank you for any help you can provide!
Use snprintf() to construct a buffer containing the int and then concatenate it to your rv buffer. Do not attempt to concatenate the existing content of rv in the same call to snprintf():
snprintf(rv, 2048, "%s%d", rv, itoa(fib(num), rv,10)));
as this means the input and output buffers overlap which is undefined behaviour.
Also:
sizeof(char) is guaranteed to be 1
see Do I cast the result of malloc?
So the malloc() call would be:
rv = malloc(2048);
if (rv)
{
}
You need either an intermediate char array to print the number to before strcating, or you can directly sprintf the number to rv, but for that you need a pointer to the end,
char *rv = malloc(2048);
char *rv_end = rv;
...
rv_end += sprintf(rv_end, "%d", fib(num));
and also update the rv_end pointer when appending other things to the buffer.
(Thanks to jthill for the improvement using the return value of sprintf.)
You could do this
sprintf(dest,"%s %d",rv,num);
char * strcat ( char * destination, const char * source );
The "const" on source simply tells the compiler that the strcat function will not modify "source". Otherwise,
strcat(buffer, "hi");
would not be allowed.
This does not mean that your source must be constant, nor should it have the const modifier.
strcat(rv, (char *)itoa(fib(num), rv,10));
(no "const") is perfectly legal and should not generate any compiler warnings.
The only issue is that if you don't know the length of the source, you're opening yourself up to a buffer overflow. In this particular case, you can figure out how long the itoa return can be and size the destination accordingly. However, it's probably safer to us snprintf to control how large it is.

C passing argument 1 of ‘fread’ makes pointer from integer without a cast

I'm sorry to bother you with this, but i'm stuck with it for too long already.
I get the following warning on fread: "warning: passing argument 1 of ‘fread’ makes pointer from integer without a cast"
I'm new to C and really like it, but don't get over this.
Thanks in advance.
typedef unsigned char byte;
int main( int argc, char *argv[]){
FILE * filein;
filein = fopen(argv[1], "r" );
int width=10;
int height=10;
byte ** data;
// Allocation
data = (byte **) malloc(height*sizeof(byte *));
for(int i=0;i<height;i++){
data[i]=(byte *) malloc(width*sizeof(byte));
}
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
fread(data[i][j], sizeof(const char), 1, infile);
}
}
for(int i=0;i<height;i++){
free(data[i]);
}
free(data);
fclose(filein);
return 0;
exit(0);
}
This is only a small piece of the actual program. The task is to read a binary pgm-image, store it in a data-matrix, normalize the values and write them to a new binary pgm-image.
fread() expects the void* pointer to the buffer. You are passing the value stored in data[i][j], not the pointer to the element data[i][j].
Try using the
fread(&data[i][j], sizeof(const char), 1, infile);
By the way, .pgm file format (if this is it) has a header and it is not sufficient to read only the width*height characters. The pixel values are also separated by spaces, so a little parsing is required. Also keep in mind that the end-of-line symbols also take space (.pgm cannot have more than 70 characters on one line)
The type of data[i][j] is byte. That is not a pointer. What you really want is to read to &data[i][j], if you're reading one byte at a time.
To read a single char, you can do
int c; // not char, because it must be able to hold EOF
c = getc(infile);
if( c == EOF ){
// do something about error or premature EOF
}
data[i][j] = c;
This is more verbose mostly because of the error checking that your code doesn't do.

"stack smashing detected" by getline & printf - and i really cant figure out why

when it comes to C i am not a noob - i'm more like a total & complete stupid ignorant noob! i am trying to write a program to parse simple text files, and i would like to make it as general as possible(why i use getline). well here is my code:
//afile.c
#include <stdio.h>
#include <stdlib.h>
main( )
{FILE *fp1;
char *filename;
char **line;
size_t *sz;
int s;
filename = "n";
if ((fp1 = fopen(filename,"r")) == NULL ){printf("error...");return 1;}
do {
s = getline(&line,sz,fp1);
if (s != -1)
printf(" %s \n",line);//<-- %s seems to be wrong! %*s removes the gcc warning
} while (s != EOF);
fclose(fp1);}
I am pretty sure its some pointer allocation problem, but i really cant figure out where it is. i've found out that replacing %s with %s makes the compiler warning disappear, but it results in an infinity of \t(tabs) being written in the terminal.
By the way, the error message i get is:
stack smashing detected *: ./afile terminated
Segmentation fault
getline expects an argument of type char**, and you supplied &line, which is char***. Additionally, getline acts on the current value of the value its first arguments points to (so, the value of line), and you didn't initialize it. Change your program to:
char *line = NULL;
and it should be fine.
You failed to initialize the line variable, and it contains a random value. Readline probably tries to realloc() it.
UPDATE: the definition for line is also wrong, only one asterix needed, as pointed out by others.
int main(void )
{
FILE *fp1;
char *filename;
char *line = NULL; /* <- here */
size_t *sz;
int s;
...
}
Your pointer redirections are inconsistent. The variable line is declared:
char **line;
Which is a pointer to a pointer to a character, or a pointer to a string. getline() expects a pointer to a string, but you pass &line - a pointer to a pointer to a string.
Finally, your printf() format specified is %s, do it wants to format a string, but you give it a pointer to a string.
Long story short: remove an asterisk to create
char *line;

Resources