Consider the following code:
#include <stdio.h>
void f(const char * str) {
str = "java";
}
void main (int argc, char * argv[]) {
const char *str = "erlang";
f(str);
printf("%s\n", str);
}
The output is "erlang" and I don't quite know why..
My current knowledge says that string literals "erlang" and "java" are both stored in the process adress space, within section "constants". And according to this, the fucntion f should change the pointer to point to "java", but this doesn't happen. Could someone please explain what is going on here?
Because function arguments are passed by value in C and modifying arguments in callee won't affece caller's local variables.
Use pointers to modify caller's local variables.
#include <stdio.h>
void f(const char ** str) { /* add * to declare pointer */
*str = "java"; /* add * to access what is pointed */
}
int main (int argc, char * argv[]) { /* use standard signature */
const char *str = "erlang";
f(&str); /* add & to get a pointer pointing at str */
printf("%s\n", str);
}
C has copy by value. When str is passed as an argument to f, it is copied first, and that very copy is actually passed to f. Assigning "java" to that copy doesn't do anything to the original str in main.
Since you are passing the value that means call by value you will see the output as java if you pass the reference like this:
#include <stdio.h>
void f(const char ** str) {
*str = "java";
}
void main (int argc, char * argv[]) {
const char *str = "erlang";
f(&str);
printf("%s\n", str);
}
output:
rabi#rabi-VirtualBox:~/rabi/c$ gcc ptr1.c
rabi#rabi-VirtualBox:~/rabi/c$ ./a.out
java
Function parameters are its local variables. You can imagine the function definition and its call the following way (I changed the name of the parameter from str to s for clearity)
void f(/*const char * s*/) {
const char *s = str;
s = "java";
}
//...
const char *str = "erlang";
f(str);
Any changes of the local variable s does not influence on the original variable str used as the argument. The variable str itself was unchanged.
You should pass arguments by reference if you are going to change them in a function. For example
#include <stdio.h>
void f( const char ** str )
{
*str = "java";
}
int main( void )
{
const char *str = "erlang";
f( &str );
printf( "%s\n", str );
}
The program output is
java
Take into account that according to the C Standard function main shall have return type int.
Could someone please explain what is going on here?
Many good answers all ready yet thought I'd try to perform a detailed walk-though with OP with slightly modified code.
Consider what happens with f("Hello World"). "Hello World" is a string literal. It initializes a char array. When an array is passed to a function or assigned to a pointer, it is converted to the address of the first element of the array. f() receives a copy of the address of 'H' in its str. #1 prints "Hello World". str is re-assigned to the address of 'j'. #2 prints "java". The function ends without affecting "Hello World".
With str = "erlang", str receives the address of the 'e'. #3 prints "erlang". On the function call, the value of main()'s str is copied to the f()'s str. #1 prints "erlang". Like before, str is re-assigned to the address of 'j'. #2 prints "java". The function ends without affecting main()'s str. #4 prints "erlang".
#include <stdio.h>
void f(const char * str) {
printf("f() before str='%s'\n", str); // #1
str = "java";
printf("f() after str='%s'\n", str); // #2
}
int main(void) {
f("Hello World");
puts("");
const char *str = "erlang";
printf("main() before str='%s'\n", str); // #3
f(str);
printf("main() after str='%s'\n", str); // #4
return 0;
}
Output
f() before str='Hello World'
f() after str='java'
main() before str='erlang'
f() before str='erlang'
f() after str='java'
main() after str='erlang'
As to OP's question:
C literals, where are these stored?
The location of a string literal is not defined in C. It might use the "process address space, within section constants", it might not. What is important is that an array is formed and the address of the first character is given in assignment to a const char *. Further detail: writing to this address is undefined behavior (UB), it may "work", fail, seg-fault, etc.
This would be more obvious if you changed the name of the argument for f...
#include <stdio.h>
void f(const char * foo) {
foo = "java";
}
int main (int argc, char * argv[]) {
const char *str = "erlang";
f(str);
printf("%s\n", str);
}
foo is a different variable to str. It has a different name, a different scope, and can contain a different value. The changes to foo won't propagate to str. If you wanted to modify str from within f, you would need to make f look like this:
void f(const char **foo) {
*foo = "java";
}
... and pass a pointer to str to f like so: f(&str);.
Did you happen to notice how I changed void main to int main? There are only two signatures for main entry points (excluding the equivalents) that are guaranteed by the standard to be portable:
int main(void) { /* ... */ }
... and ...
int main(int argc, char *argv[]) { /* ... */ }
Either way, main always returns int (or equivalent). This shouldn't inconvenience you too much, as in C99 (any half-decent compiler that's newer than fifteen years old) and C11 there's this little gem which allows you to omit return 0; from main:
If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument;11) reaching the } that terminates the main function returns a value of 0.
So if anything, your code using an int main entry point is not just portable but also one byte shorter than your code using a non-portable void main entry point.
Related
I'm experimenting with using dynamic libraries and C on Linux. The following code will print wrong ouput:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **arg)
{
void *dl = dlopen("./lib.so", RTLD_NOW);
if (!dl) {
fprintf(stderr, "ERROR: %s\n", dlerror());
exit(1);
}
char *ver = dlsym(dl, "show_version");
printf("%s\n", ver);
}
If I make the following change the output will be correct:
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **arg)
{
void *dl = dlopen("./lib.so", RTLD_NOW);
if (!dl) {
fprintf(stderr, "ERROR: %s\n", dlerror());
exit(1);
}
char *(*ver)() = dlsym(dl, "show_version");
printf("%s\n", ver());
}
I'm not sure what char *(*ver)() is doing and why it's needed? Can anyone explain?
dlsym(dl, "show_version") returns the address for the symbol show_version. Since show_version is a function, that is the address of the function.
char *ver = dlsym(…); puts that pointer in a char *, which is basically useless. The pointer to a function does not point to bytes that are useful to print. Then printf("%s\n", ver); says to print the bytes that ver points to as if they were a string. But the bytes there are (in a typical C implementation) machine code for the function. They are not the bytes of a character string you want to print.
char *(*ver)() = dlsym(…); defines ver to be a pointer to a function whose arguments are not specified and that returns a char *. To see this:
char something declares something to be a char.
char *something declares something to be a pointer to a char.
char *something() declares something to be a function whose arguments are not specified that returns a pointer to char.
char *(*something)() declares something to a pointer to such a function.
Then, in printf("%s\n", ver());, ver() calls this function. The char * it returns is passed to printf to be printed.
dlsym - obtain address of a symbol in a shared object or executable
This means that when you do dlsym(dl, "show_version"); you are not actually calling the function show_version in your shared library. You obtain the address of that function - which can be used to call the function over and over again.
To "decode" what char *(*ver)() means, you can use what is often called the Clockwise/Spiral Rule
+-----+
| V
char* (*ver) () ver is a
^ ^ | | pointer to
| | | | a function (taking no arguments)
| +-+ | returning char*
| |
+------------+
I assume the above matches the signature of the show_version function that you put in the shared library. Example:
// a function (taking no arguments), returning a char*
char *show_version(void) {
static char version[] = "1.0";
return version;
}
Using the same rule on your first attempt, char* ver:
char* ver
^ | ver is a
| | char*
+----+
You need a pointer to a function (with the correct signature) to be able to call the function and get the result you want. You can't call a char* and when you do printf("%s\n", ver); it'll just start reading the memory at the address (where your function is stored) until it finds a null terminator. You probably see just gibberish.
If you on the other hand have a proper function pointer, you can as you've noticed, call the function it points at with ver() and you get a char* in return which points at the string your dynamically loaded function returned.
You can also use function pointers in your programs without involving shared libraries.
#include <stdio.h>
long foo(short x, int y) {
return x + y;
}
int main() {
long(*foo_ptr)(short, int) = foo;
// foo_ptr is a pointer to a function taking (short, int) as
// arguments and returning a long
printf("%ld\n", foo(1, 2) ); // prints 3
printf("%ld\n", foo_ptr(1, 2) ); // also prints 3
}
Im writing uart code for a microcontroller. My code is working fine with passing string("..."), but giving problem when i try to passing the char('.'). Yes, it is i've declared "s" a pointer for string, but there is a chance of passing char value. Is there any possibilities to pass both string and char values in send_data?
#include <stdio.h>
void send_data( char *s)
{
while(*s!='\0')
{
send_dt1(*s);
s++;
}
}
void send_dt1( char in_c)
{
printf("%c",in_c);
}
int main(void)
{
send_data("sample_data"); //fine
send_data('Q'); //warning, no data displaying
return 0; /* terminate the program */
}
Thanks..
As send_data expects s to be a null-terminated string anyway, I would just recommend using a string:
send_data("Q");
Notes:
you should pass a const char* around as long as you don't modify the string at s
send_data('Q'); //warning, no data displaying
Don't pass character to it . It expects a char * .
You can do this -
send_data("Q");
Also you should declare a prototype for function void send_dt1( char in_c) as you make a call to it before it's definition in function void send_data( char *s) .
This will also generate warning.
You can't pass a char to a function that expects a char *.
If you really need to be able to pass both, you'll need another function to accept a char and put it in a string so that send_data can be called:
void send_char_data(char c)
{
char s[2];
s[0] = c;
s[1] = '\0';
send_data(s);
}
....
int main(void)
{
send_data("sample_data");
send_char_data('Q');
return 0;
}
Say I have this function:
void f(char *s) {
s[0] = 'x';
}
This function will sometimes cause errors and sometimes not. For example,
char *s = "test";
f(s); // Error
char t[] = "test";
f(t); // Success
Inside function f, is it possible to determine whether or not s[0] = 'x'; will cause an error before doing it?
The responsibility is on the caller to comply with requirements of the function that the argument be changeable, not the reverse.
const char *s = "test"; // tell compiler s is immutable
f(s); // compilation error since f() requires a non-const argument
char t[] = "test";
f(t); // Success
The only way to stop the compiler from rejecting f(s) in the above is to either remove the const from the declaration of s, or to cast the const'ness away. Except in exceedingly rare circumstances, both are positive indicators of a problem.
Note: it is an anomaly in the language that s can be declared without the const qualifier. Get in the practice of using const where needed (e.g. when initialisating a pointer using a string literal). A lot of program bugs are eliminated that way.
Given the feedback I've received, It sounds like I should instead document whether or not the function requires its arguments to be mutable. For example:
#include <stdio.h>
#include <string.h>
char* f(char*);
void g(char*);
int main(int argc, char *argv[]) {
// s is immutable.
char *s = "test";
puts(f(s));
// t is mutable.
char t[] = "test";
g(t);
puts(t);
}
/*
* Returns a copy of the given string where the first character is replaced with 'x'.
* Parameter s must contain at least one character.
*/
char* f(char *s) {
char *t = strdup(s);
t[0] = 'x';
return t;
}
/*
* Replaces the first character of the given sting with 'x'.
* Parameter s must contain at least one character and be mutable.
*/
void g(char *s) {
s[0] = 'x';
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *abc = "abc";
char *new_str;
new_str = getStr(&abc);
printf("%s", abc);
}
char *getStr(char *str)
{
printf(str);
return str;
}
What's wrong with the code above?
A bunch of small things:
You're passing &abc to getStr(). &abc is a pointer to the variable that is holding your string. Its type a pointer to a pointer, and that's incompatible with the char *str argument of getStr().
Your getStr() is defined after it is used. You either need to move its definition to before main() or add a prototype before main().
The type of a string literal like "abc" is const char *. You're defining a variable of type char *, which is dubious (since it would allow you to modify a string literal, which is not allowed).
here is a working version:
#include <stdio.h>
#include <stdlib.h>
char *getStr(char *str);
int main(void)
{
char *abc = "abc";
char *new_str;
new_str = getStr(abc);
//printf("%s", *abc); don't really need this
}
char *getStr(char *str) {
printf("%s", str);
return str;
}
Here's a list of problems with the old one:
No prototype for your function getStr.
You can't do printf(str) you need a "%s" as an additional param.
You need to change: new_str = getStr(&abc) to new_str = getStr(abc)
A C string is a pointer to a character that begins a sequence of characters that end with a null byte. The variable abc perfectly fits this definition.
Also, abc is of type pointer to character. You are passing the address of abc, ie getStr will be receiving the address of a pointer to a character--so getStr's only argument should be of type pointer to pointer to character. The types do not match up.
EDIT: Also, getStr is called before it's declared. Your compiler may allow this, but it's bad practice for many reasons. You should declare it or define it before it is used. If you are using gcc as your compiler, always use
gcc -ansi -Wall -pedantic
Those three flags will conform to ANSI standards, and it would either yell at you for the above issues or not compile.
Your getStr function takes a char *, but that's not what you're passing it - you're passing the address of the pointer, instead of passing it the actual pointer.
Change:
new_str = getStr(&abc);
to:
new_str = getStr(abc);
and I think it'll work.
char *test = "hello";
test = change_test("world");
printf("%s",test);
char* change_test(char *n){
printf("change: %s",n);
return n;
}
im trying to pass a 'string' back to a char pointer using a function but get the following error:
assignment makes pointer from integer without a cast
what am i doing wrong?
A function used without forward declaration will be considered having signature int (...). You should either forward-declare it:
char* change_test(char*);
...
char* test = "hello";
// etc.
or just move the definition change_test before where you call it.
printf() prints the text to the console but does not change n. Use this code instead:
char *change_test(char *n) {
char *result = new char[256];
sprintf(result, "change: %s", n);
return result;
}
// Do not forget to call delete[] on the value returned from change_test
Also add the declaration of change_test() before calling it:
char *change_test(char *n);
You're converting an integer to a pointer somewhere. Your code is incomplete, but at a guess I'd say it's that you're not defining change_test() before you use it, so the C compiler guesses at its type (and assumes it returns an integer.) Declare change_test() before calling it, like so:
char *change_test(char *n);
thanks a bunch guys! didnt think i would have this problem solved by lunch. here is the final test class
/* standard libraries */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* change_test(char*);
int main(){
char *test = "hello";
test = change_test("world");
printf("%s",test);
return (EXIT_SUCCESS);
}
char* change_test(char *n){
return n;
}