sprintf can't change static variable inside printf call - c

I can't seem to understand what exactly is going on here
#include <stdio.h>
const char* mes(int a)
{
static char mess[100];
sprintf(mess, "%d", a);
return mess;
}
const int* hes(int a)
{
static int arr[100];
arr[0] = a;
return arr;
}
int main()
{
printf("%s %s\n", mes(55), mes(25)); //55 55
printf("%s\n", mes(55)); //55
printf("%s\n", mes(25)); //25
printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}
In the first printf the second function seems to be ignored and the output of the earlier input gets printed again.
At first I assumed it was a static variable issue, so I tried printing them separately and then they seem to work fine.
Then I assumed it was a printf issue so I tried to simulate the same behavior with an integer array, and it worked fine there too.
I've run this program a couple of times with various inputs, ruling out the possibility of UB.
So, what exactly am I missing here?
EDIT:
I encountered this issue somewhere else and couldn't understand what was happening. So I reproduced the issue in a short sample code. But my question stands, (as many have mentioned) are all the parameters evaluated before printing? If so there should be an overwrite in both cases (int and char array) regardless of evaluation order.

The order of evaluation of function parameters is unspecified, meaning that you can theoretically see 25 25 instead. That's first thing.
Second, when printf is called, both functions have been already evaluated, and the same pointer is passed as first and the second string (because it is a static location), which is the result of the last evaluation (55 in your case). So identical text is printed.
This is pretty much equivalent to the following:
char* a = mes(25);
char* b = mes(55);
// Note, the above can swap depending on the order of evaluation
printf("%s %s\n", a, b);
But here a equals b as both are pointing to a static array.
As for the second example this is not happening as it will be equivalent to the following (up to the order of evaluation):
int *ap = hes(55);
int a = ap[0];
int *bp = hes(25);
int b = bp[0];
printf("%d %d\n", a, b);
Note, that here the pointed values are passed and not the pointer itself. So even though ap equals bp, a is not the same as b.

The why code is wrong is well explained by others.
An alternative to static char mess[100] is to use a compound literal as an argument. Then the return value from mes(), hes() is valid until the end of the block of code - well after the printf().
#include <stdio.h>
const char* mes_helper(char mess[100], int a) {
sprintf(mess, "%d", a);
return mess;
}
const int* hes_helper(int arr[100], int a) {
arr[0] = a;
return arr;
}
// compound literal v-------------v
#define mes(a) mes_helper( (char [100]){0}, (a))
#define hes(a) hes_helper( (int [100]){0}, (a))
// No changes to `main() code
int main(void) {
printf("%s %s\n", mes(55), mes(25)); //55 55
printf("%s\n", mes(55)); //55
printf("%s\n", mes(25)); //25
printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}
Output
55 25
55
25
55 25

There is only one variable mess.
When you call:
printf("%s %s\n", mes(55), mes(25));
You are filling in that one variable two different times, once with "25" and once with "55" (overwriting the "25"). As a result, when printf goes to format with %s %s, it finds the same string twice, "55" and again "55" because the "25" has already been overwritten.
You need to be aware that all parameters to a function are evaluated before the function is called. The order of parameter evaluation is not defined, but is often right-to-left. Breaking down that printf into little steps:
Evaluate the mes(25); now static char mess is now "25".
Evaluate the mes(55), now static char mess is overwritten to "55".
Evaluate the parameter "%s %s\n" (there isn't much to evaluate here. Its just a string)
Call printf with parameters: "%s %s\n", and "55" and "55"

When you call:
printf("%s %s\n", mes(55), mes(25));
mes(55) and mes(25) get evaluated before their result is populated into the string. And since you're pointing to the same static memory, when it's time to populate the string, you get the same value.

Returning the address of a static variable is almost always a bad pattern as you'e found. There's only one such static allocation for the whole program, so for example calling mes twice causes the second call to overwrite the results of the first.
A more reasonable pattern is having the caller furnish a buffer. This is also needed for thread safety:
#include <stdio.h>
const char* mes(char *buf, int a)
{
sprintf(buf, "%d", a);
return buf;
}
const int* hes(int *arr, char *buf, int a)
{
arr[0] = a;
return arr;
}
int main()
{
char buf1[100], buf2[100];
int arr[100];
printf("%s %s\n", mes(buf1, 55), mes(buf2, 25)); //55 55
printf("%s\n", mes(buf1, 55)); //55
printf("%s\n", mes(buf1, 25)); //25
printf("%d %d\n", hes(arr, 55)[0], hes(arr, 25)[0]); //55 25
}

Related

error: result of comparison against a string literal is unspecified for C [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Closed 2 years ago.
So I am trying to create a little joke with C and I donĀ“t know what I am doing wrong. I see the Error "error: result of comparison against a string literal is unspecified" and do not know how to fix it, can someone help.
#include<stdio.h>
#include<cs50.h>
int main(void){
string a = get_string("ENTER YOUR NAME FOR READING\n");
if (a == "david")
;
{
printf("...");
}
}
Your code has three issues:
1.
if (a == "david")
a decays to a pointer to the first element of array a.
"david" is a string literal.
With the if Statement, you attempt to compare the address of the array a by the address of the string literal "david". String comparison dies not work that way in C.
Use strcmp() - header string.h to compare strings.
strcmp() returns 0 if the strings are equal. To make the if condition turn true you need to use ! negation operator.
2.
There is a misplaced ; between the if condition and if body. Remove it.
3.
The use of cs50.h is deprecated. If you are not required to use it explicitly, leave your hands off from it.
#include <stdio.h>
#include <string.h>
int main (void) {
char a[20];
printf("ENTER YOUR NAME FOR READING:\n");
if (!fgets(a, sizeof a, stdin))
{
fputs("Failure at input", stderr);
// further Error routine.
}
a[strcspn(a, "\n")] = 0;
if (!strcmp(a,"david"))
{
printf("...");
}
}
You cannot compare two strings in c using a comparison operator.
Try using
strcmp(const char* one, const char* two)
That is, in your case
use
#include<stdio.h>
#include<cs50.h>
int main(void){
char[] a = get_string("ENTER YOUR NAME FOR READING\n");
char[] b = "david";
if (!strcmp(a,"david"))
{
printf("...");
}
}
Also, Strings in c are not declared as strings. They are declared as char arrays
Comparing two char pointers like this char *a, *b; a == b; is accepted by the compiler, but if you are using such a syntax to compare the string values, then you may not get the results you are expecting. Some times it may evaluate to true or sometimes to false. The reason is, the program checks whether a and b are pointing to the same address, and not for if they have the same string value. Consider the program below.
#include <stdio.h>
int main()
{
char* a = "test";
char* b;
if ((b="test") == a)
printf("Impossible\n");
else
printf("Thought so\n");
printf("%p %p\n", a, b);
return 0;
}
OUTPUT
Impossible
0x55b6bd52d004 0x55b6bd52d004
As you see from the output, the if block is evaluated to true. The reason is also in the output; a and b are pointing to a similar address. Why?
It comes from the string-pooling, one of the compiler optimization. Even though a and b are initialized at different points in the program, as they are referring to the same string constant, the string constant is initialized only once by the compiler.
Here is the objdump of the executable.
$ objdump -s -j .rodata
a.out: file format elf64-x86-64
Contents of section .rodata:
2000 01000200 74657374 00496d70 6f737369 ....test.Impossi
2010 626c6500 54686f75 67687420 736f0025 ble.Thought so.%
2020 70202570 0a00 p %p..
test is initialized only once. For a quick check, consider this program's output and its objdump .
#include <stdio.h>
int main()
{
char* a = "test";
char* b;
if ((b="test1") == a)
printf("Impossible\n");
else
printf("Thought so\n");
printf("%p %p\n", a, b);
return 0;
}
OUTPUT
Thought so
0x557a96aa9004 0x557a96aa9009
objdump
$ objdump -s -j .rodata a.out
a.out: file format elf64-x86-64
Contents of section .rodata:
2000 01000200 74657374 00746573 74310049 ....test.test1.I
2010 6d706f73 7369626c 65005468 6f756768 mpossible.Though
2020 7420736f 00257020 25700a00 t so.%p %p..
It is evident from the output that a and bare pointing to different locations, and both are having different string values that are initialized by the compiler separately. So, if a and b are having similar string values, does a==b always evaluate to true? Consider the below program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char* a = malloc(sizeof(char) * 5);
strcpy(a, "test");
char* b;
if ((b="test") == a)
printf("Impossible\n");
else
printf("Thought so\n");
printf("%p %p\n", a, b);
return 0;
}
OUTPUT
Thought so
0x5607d9938260 0x5607d91fb004
Even though the a and b are having the same string values, if evaluate to false. Why? The address a is pointing, which is created by malloc() belongs to the heap section of the executable, whereas the address b is pointing belongs to the data section of the executable, and they are indeed different.
So what should we do to compare the string values?
Well, it is straight forward, there are already library functions which can perform this task. You can use them withstring.h. Consider the below code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char* a = malloc(sizeof(char) * 5);
strcpy(a, "test");
char* b = "test";
char* c;
if (strcmp((c="test"), a) == 0)
printf("Impossible\n");
else
printf("Thought so\n");
if (strcmp(b, a) == 0)
printf("Impossible\n");
else
printf("Thought so\n");
if (strcmp(b, c) == 0)
printf("Impossible\n");
else
printf("Thought so\n");
printf("%p %p %p\n", a, b, c);
return 0;
}
OUTPUT
Impossible
Impossible
Impossible
0x556debf41260 0x556deac28004 0x556deac28004
Irrespective of where a, b and c are pointing if they all are having the same string values, then strcmp() returns 0.

Use pointer arithmetic to change the value of a variable in another function.

#include <stdio.h>
void interrupt();
int main() {
int n = 8;
char c = 'Z';
interrupt();
printf("%d%c\n", n, c);
}
void interrupt() {
printf("Run normally\n");
//ADD CODE...
}
As it stands, this function will output "8Z" regardless of what the interrupt() method does. I am attempting to modify it such that it prints "3Y" without changing anything in the original main function, and only using the interrupt() function (not allowed to pass arguments!). I am allowed to use variables in the interrupt() function, but I am confused to as how you obtain the addresses of 'n' and 'c' without making a global variable, which would defeat the purpose of this question. Since the address of the stack changes every time this runs, there doesn't seem to be a way to do pointer arithmetic (which is what I need to do), and therefore I'm a bit confused and stuck.
Disclaimer: Don't attempt to use this in production code
I took this as a puzzle and proceeded to solve it. You said you are not allowed to modify main. I took the liberty of modifying main slightly -- to print the addresses of n and c.
int main()
{
int n = 8;
char c = 'Z';
printf("%p %p\n", &n, &c);
interrupt();
printf("%d%c\n", n, c);
}
I also modified interrupt a little bit, also to print the values of an address.
void interrupt() {
int i = 10;
char* np = (char*)&i;
char* cp = (char*)&i;
printf("%p %p\n", np, cp);
printf("%p\n", &i);
}
When I ran the program, I got the following output:
0x22cb0c 0x22cb0b
0x22cabc 0x22cabc
0x22cabc
8Z
From the output, I am able to compute the offset between &n in main and &i in interrupt, and the offset between &c in main and &i in interrupt. Now I can manipulate the offset to make np and cp in interrupt to point to n and c in main.
void interrupt() {
int i = 10;
char* np = (char*)&i;
char* cp = (char*)&i;
np += (0x22cb0c - 0x22cabc);
cp += (0x22cb0b - 0x22cabc);
*(int*)np = 3;
*cp = 'Y';
printf("%p %p\n", np, cp);
printf("%p\n", &i);
}
With the changes to interrupt, I get the following output:
0x22cb0c 0x22cb0b
0x22cb0c 0x22cb0b
0x22cabc
3Y
Mission accomplished by changing main a little bit. If you are not allowed to change it at all, you'll have to use a different program to compute the offsets.
By what #Alex Skalozub said, you can get the stack offset and modify the local variables in the calling function.
You can:
1. Study the compiler and OS manual to calculate the stack offset.
Or
2. Write a little function to get it at run-time. Like below.
int get_stack_offset(void)
{
long dummy1;
return dummy_call(&dummy1) + sizeof(dummy1);
}
int dummy_call(int address)
{
long dummy2;
return &dummy2 - address;
}
Then you can just
void interrupt() {
printf("Run normally\n");
int stack_offset = get_stack_offset();
char* c_address = (char*) (&stack_offset - stack_offset);
int* n_address = (int*) (c_address - sizeof(char));
// Then, modify them
*c_address = 'Y';
*n_address = 3;
// Other
// ...
}
*Assuming the stack is increasing. When it is decreasing you need reverse the +/- operator.
*I don't consider about alignment, which maybe you need to.
*And here is a great explanation you can reference.
Does stack grow upward or downward?

Printing integers passed as arguments in function in C

I am very new to C programming and having trouble compiling what should be a very simple function. The function, called printSummary, simply takes 3 integers as arguments, then prints some text along with those integers. For example, if hits=1, misses=2, and evictions=3, then printSummary(hits,misses,evictions) should print the following:
hits:1 misses:2 evictions:3
Here is the code I'm using. Thanks in advance for any advice.
#include<stdio.h>
void printSummary(int hits, int misses, int evictions)
{
printf('hits: %d\n');
printf('misses: %d\n');
printf('evictions: %d\n');
}
int main()
{
int hit_count = 1;
int miss_count = 2;
int eviction_count = 3;
printSummary(hit_count, miss_count, eviction_count);
return 0;
}
Compiling this code gives me several warnings, but no errors. When I run the code, I get a segmentation fault. Like I said, I am fairly new to C so there is most likely a simply solution that I am just missing. Thanks in advance for any advice.
Make the below changes .
printf("hits: %d\n",hits);
printf("misses: %d\n",misses);
printf("evictions: %d\n",evictions);
printf has a
int printf(const char *format, ...)
prototype. So in the first argument you can pass format specifiers and in the next provide the actual variables/values to be printed out
errors are:
void printSummary(int hits, int misses, int evictions)
{
/* Name: printf
Prototype: int printf (const char *template, ...)
Description:
The printf function prints the optional arguments under the
control of the template string template to the stream stdout.
It returns the number of characters printed,or a negative value if
there was an output error.*/
printf("hits: %d\n", hits); // don't use ' it is used only for char variable for example: char a = 'c';
printf("misses: %d\n", misses);
printf("evictions: %d\n", evictions);
}
Your printf function is not being called correctly. You have to include the integers needed to print:
printf("hits: %d\n", hits);
printf("misses: %d\n", misses);
printf("evictions: %d\n", evictions);
Read more about the printf function here.

Pointers in C for a rookie

I am just starting to learn programming for a unit I am doing in my engineering course and I have come across pointers. I just wanted some reassurance that I actually understand the concept correctly in terms of using a pointer as an argument in a function. If I understand it correctly, you pass a pointer to an address of a variable you would like to be altered by a separate function called, even though it is a local variable within the scope of the calling function. Does that make sense? I have an example from my text book which I re-wrote. The only thing is they gave it in two incomplete parts and I put it together, filled in the blanks and added the final printf statement in the main function. I'll paste it here:
#include <stdio.h>
#include <stdlib.h>
#define READ_OK 0
#define READ_ERROR 1
int read_num(int lo, int hi, int *num);
int main(int argc, char *argv[])
{
int lo = 0, hi = 0, *num, val;
printf("Please enter a lower bound and an upper bound for your range,respectively\nLower: ");
scanf("%d", &lo);
printf("Upper: ");
scanf("%d", &hi);
num = &val;
if(read_num(lo,hi, &val) != READ_OK)
{
printf("Read error, program abort\n");
exit(EXIT_FAILURE);
}
else
{
printf("You entered %d, press any key to continue: \n", val);
getchar();
}
return 0;
}
int read_num(int lo, int hi, int *num)
{
int next;
printf("Enter a number between %d and %d: ", lo, hi);
while(scanf("%d", &next)==1)
{
if (lo<=next && next<=hi)
{
*num = next;
return READ_OK;
}
printf("%d is not between %d and %d\nTry again: ", next, lo, hi);
}
return READ_ERROR;
}
So is my understanding correct? "val" gets modified in read_num() by passing it's address in the form of pointer "*num", in which the the value for "next" is then written?
PS: is this syntax correct?
PPS: What would this process specifically be called?
Thanks a bunch for any help :)
The *num is not necessary inside the main() function. As you are passing the address of the val inside the read_num() , so any changes from the read_num() will also affect the value inside main() as you are working with the address.
In your program you have basically use two different pointers- one is inside main which is num, and another inside read_num() which is also num, for more understanding see the scope of a variable in c. As the val is inside main so you don't need to use pointer here, because you have the access of changing the value from the main as it is local to it. You will need pointer when you will be changing the value of val outside from the main, or from outside of the scope of the variable.

c printf size_t

My code compiles, though the printf doesn't display anything?
If I take out the formatter part of the printf then it works just fine.
#include <stdio.h>
size_t MyStrlen(const char *s1)
{
const char *s1Copy = s1;
while (*s1)
{
*s1Copy++;
}
return s1Copy -s1;
}
int main(void)
{
const char str[] = "HELLO";
printf("Length: %d \n", (unsigned int)MyStrlen(str));
return 0;
}
In your loop test you want to test *s1Copy for non-NULL, not *s1, which you aren't incrementing. As it is, since *s1 never changes, you're walking right off the end of your string argument with s1Copy++ and the code won't terminate normally.
Unless of course you pass the empty string: your MyStrlen method will work for the empty string.
while (*s1Copy)
s1Copy++;
This:
printf("Length: %d \n", (unsigned int)MyStrlen(str));
is ok; %d expects an int argument, but it's guaranteed that int and unsigned int have the same representation for values that are in the range of both. This:
printf("Length: %u\n", (unsigned int)MyStrlen(str));
is better, since %u expects an unsigned int argument.
The correct format for size_t is "%zu":
printf("Length: %zu\n", MyStrlen(str));
but this is a "new" feature in C99, and there are likely still some implementations that don't support it. (In particular, Microsoft has been very slow to support C99.) For maximum portability (assuming your string is no more than 232-1 bytes long) you can use this:
printf("Length: %lu\n", (unsigned long)MyStrlen(str));
This answers the question you asked about printf; pb2q ably diagnosed the actual cause of the problem you're seeing (and beat me to it!).

Resources