I have a question regarding passing c string into function.
If I have a function:
void reverse(char* c){
//here is the reverse code
}
In the main:
int main(){
char* c1="abcd";
char c2[5]="abcd";
char * c3=new char[5];
c3="abcd";
}
In my test, only c1 is not allow to pass into the function, other two works fine. I would like to know why c1 is a wrong usage? Thank you very much!
Your code is C++, not C; new char[5] is a syntax error in C.
C and C++ are two different languages. In C++, string literals are const, and passing a string literal to a function that takes a char* argument is an error. You should have gotten an error message (which you haven't bothered to show us) from your C++ compiler.
(If you were using a C compiler, it would have accepted a call like reverse("foo"), but it would have complained about the new char[5].)
c1 will point to a string literal and attempting to modify a string literal is undefined behavior, this is based on the assumption that reverse will attempt to reverse the string in place.
Related
I'm reading a book the c programming language authored by Brian W. Kernighan and Dennis M. Ritchie.
The book lists code below
void strcpy(char *s, char *t){
while((*s = *t) != '\0'){
s++;
t++;
}
}
and says:
Because arguments are passed by value, strcpy can use the parameters
s and t in any way it pleases
which I'm not agreed with. Why above arguments are passed by value?
According to another book C how to program:
In C, you use pointers and the indirection operator to simulate
call-by reference. When calling a function with arguments that should
be modified, the addresses of the arguments are passed.
In latter point of view, it's definitely call-by-reference.
Please tell me which way is correct and why, thanks!
btw, after assignment *s = *t, which one is compared with '\0'? *s or *t?
C passes arguments by value, always, meaning that the called function receives a local copy of whatever the caller refers to.
The called function can modify the received value, because it is a local copy, without affecting the original value. For example:
char *test(char *s) {
s++;
return s;
}
t = test("A");
it's legal and shows that the parameter s can be modified without affecting the caller (which passes a literal...).
But the strcpy() of you example does something different: it takes a pointer s, and modifies what s points to. Pointers are powerful, and they can be used to simulate "pass by reference" (a pointer is a reference).
after assignment *s = *t is held, which one is compared with '\0'? *s or *t?
The *s: in C, an assignment returns a value - and the value is the value of the assigned variable after the assignment is done. Writing:
if (i=3) ...
is the same as
i=3;
if (i) ...
If i was a pointer the syntax would be different but the mechanism is the same, the assignment would "use" the pointer and the value of the whole assignment is used as expression to be evaluated in the test.
Many people consider that idiom to be something that’s neither call-by-value or call-by-reference. For what it’s worth, Kernighan and Ritchie’s The C Programming Language does call arrays and pointers in C “references,” although it uses only the term call-by-value and not call-by-reference. That usage seems to have gone out of fashion once C++ added a different language feature called references.
A function that accepts a pointer and dereferences it will generally compile to the same machine code as a function in a language with call-by-reference. One difference that is not merely the absence of syntactic sugar: if the pointer argument itself is not const, it is possible to reassign a new value to it and make it reference something else, which “call-by-reference” semantics do not allow. In a language with call-by-reference, you could not write s++ or t++ to make the function arguments reference different objects! (C strings are stored in such a way that you can add an offset to the pointer to obtain a substring, whereas most langauges store the length of the string in the first few bytes of its memory.) However, you can mostly think of a reference T& in C++ as equivalent to passing a T *const that is guaranteed not to be NULL, and that has an invisible asterisk in front of its name.
This bothers me. It gives me a warning of
passing argument 1 of ‘funcName’ discards qualifiers from pointer target type
however, the program to run just fine and printing the submitted value.
The functions are the following
void funcName(char *str) {
printf("%s", str);
}
void main() {
funcName("Hello world");
}
output is Hello world.
It's because "Hello, world" is constant, so change the function to
void funcName(const char *text)
{
printf("%s\n", text);
}
String literals are constant, they are stored in a read only memory section of your program, passing the pointer without const means that you can accidentally modify it inside the target function, if you do so, that would cause undefined behavior, and the compiler is trying to protect you from that.
Also, void main() is not a standard compliant valid signature for main(), you can find it in old books, previous to the standard, but now it's no longer accepted, accepted and standard signatures are
int main(void) If you don't handle command line arguments.
int main(int argc, char **argv) To handle argc parameteres stored in argv that where passed in the command line.
It seems that the problem is that this C program is compiled as a C++ program.
In C++ string literals have types of constant character arrays. So if in a C++ program you supply a string literal as an argument to a function that has the corresponding parameter without the qualifier const then the compiler will issue a message.
If to compile the program as a C program then the code is valid because in C string literals have types of non-constant character arrays and the compiler should not issue a diagnostic message relative to qualifiers.
Nevertheless in any case it is better to declare the function like
void funcName( const char *str );
^^^^^^
because in this case the user of the function can be sure that the string passed to the function will not be changed.
Take into account that function main without parameters shall be declared in C like
int main( void )
For example, why not:
char *s= "example";
instead of:
const char *s= "example";
I understand that const makes it unchangeable, but why do I receive an error when compiling the first?
Additionally, how does the concept apply to
int * x;
vs
const int *x;
I see the second used a lot more, is it good practice to use "cons int *"?
There's no requirement to use const, but it's a good idea.
In C, a string literal is an expression of type char[N], where N is the length of the string plus 1 (for the terminating '\0' null character). But attempting to modify the array that corresponds to the string literal has undefined behavior. Many compilers arrange for that array to be stored in read-only memory (not physical ROM, but memory that's marked read-only by the operating system). (An array expression is, in most contexts converted to a pointer expression referring to the initial element of the array object.)
It would have made more sense to make string literals const, but the const keyword did not exist in old versions of C, and it would have broken existing code. (C++ did make string literals const).
This:
char *s= "example"; /* not recommended */
is actually perfectly valid in C, but it's potentially dangerous. If, after this declaration, you do:
s[0] = 'E';
then you're attempting to modify the string literal, and the behavior is undefined.
This:
const char *s= "example"; /* recommended */
is also valid; the char* value that results from evaluating the string literal is safely and quietly converted to const char*. And it's generally better than the first version because it lets the compiler warn you if you attempt to modify the string literal (it's better to catch errors at compile time than at run time).
If you get an error on your first example, then it's likely that you're inadvertently compiling your code as C++ rather than as C -- or that you're using gcc's -Wwrite-strings option or something similar. (-Wwrite-strings makes string literals const; it can improve safety, but it can also cause gcc to reject, or at least warn about, valid C code.)
With Visual Studio 2015 at warning level 4, this compiles and runs whether compiled as C or C++:
#include <stdio.h>
char *s1= "example\n";
const char *s2= "example\n";
int main(int argc, char **argv)
{
printf(s1); // prints "example"
s1[2] = 'x';
printf(s1); // prints "exxmple"
printf(s2);
return 0;
}
If I add this line, it will fail to compile as C or C++ with every compiler I know of:
s2[2] = 'x'; // produces compile error
This is the error the const keyword is designed to avoid. It simply tells the compiler not to allow assignments to the object pointed to.
It doesn't matter if your pointer points to char or int or anything else. The const keyword has the same effect on all pointers, and that's to make it impossible (well, very hard) to assign to the thing declared const.
A string literal used as a value compiles to an array of char that should not be modified. Attempting to modify it invokes undefined behavior. For historical reasons of backward compatibility, its type is char [] although is really should be const char []. You can enable extra compiler warnings to change this and instruct the compiler to consider such strings to be const.
As you know in C, we can initialize string variables like this:
char text[1024] =
"Hello "
"World";
But what if I have a function that returns the word "World"?
char text[1024] =
"Hello "
World();
It seems to me that's not possible in C.
Please confirm.
What you want is not possible.
The L-value to the assigment operator needs to be modifyable, which an array isn't.
From the C11-Standard:
6.5.16/2
An assignment operator shall have a modifiable lvalue as its left operand.
The only exception to this is during initialisation when using literals as R-value:
char text[1024] = "Hello ""World";
From the C11-Standard:
6.7.9/14
An array of character type may be initialized by a character string literal or UTF−8 string
literal, optionally enclosed in braces. Successive bytes of the string literal (including the
terminating null character if there is room or if the array is of unknown size) initialize the
elements of the array.
If World() is something that always returns "World", then define it as a macro:
#define World "World"
And then do:
char text[1024] =
"Hello "
World; //Without parentheses
EDIT
String concatenation in the way you expect to do is made by the C preprocessor.You are actually looking for a runtime concatenation of two strings, which can be performed in multiple ways. The simplest one is achieved by strcat function, but the initialization should be performed explicitly by a function:
char text[1024];
void init_text() {
strcpy(text, "Hello ");
strcat(text, World()); //World() defined somewhere else
}
Alternative using sprintf :
void init_text() {
sprintf(text, "Hello %s", World());
}
Then in the main function, call init_text() at the beginning:
int main() {
init_text();
...
}
It is not possible in standard C to initialize something with some runtime specific behavior. So the standard portable way is to initialize the data by calling a function at the beginning of main, as answered by Claudix.
However, if you are using some recent GCC compiler (or Clang/LLVM) you could otherwise, on some systems (including Linux and probably other POSIX systems), use some constructor attribute on function. So you would declare:
static void init_text(void) __attribute__((constructor));
and define init_text like in Claudix's answer without having to call it in your main : since it has the constructor attribute, it would be called magically before main, or during dlopen(3) if it appears inside a dynamically linked plugin or library.
A more portable trick might be to have a function returning that text which will initialize it during its first call. So instead of using text you would call get_my_text() everywhere (perhaps by putting #define text get_my_text() in a header, but I don't recommend doing so for readability reasons, so replace every occurrence of text by get_my_text() ...), and define it as:
const char*get_my_text() {
static char textbuf[1024];
if (textbuf[0]) {
// already initialized, so return it
return textbuf;
};
snprintf(textbuf, sizeof(textbuf), "Hello %s", World());
return textbuf;
}
Beware that such a trick is not reliable in multi-threaded programs: two threads might run get_my_text exactly at the same time, and you have a data race. In a multi-threaded app use e.g. pthread_once
You could even define get_my_text as a static inline function in your header file.
PS Always prefer snprintf(3) to sprintf to avoid buffer overflows. Also notice that in standard C++ any static or global data with some given constructor is initialized before main ... hence the name of GCC function attribute...
I haven't programmed in C for awhile and having an issue with passing a string to a function.
The code works however I get warnings from gcc.
I call the function in my main with:
copyToCode(code, section1, section2);
The function is:
void copyToCode (char **code, char *loc, char *data ){}
I get "conflicting types for copyToCode" on the line containing the function and "previous implicit declaration of copyToCode was here" warning on the line calling the function.
I have declared the variables:
char *code = malloc (32*1000* sizeof(char));
char *section1 = malloc(8*sizeof(char)), *section2 = malloc(8*sizeof(char));
I also tried this :
char *section1[8];
As a side question - which is correct?
The section1 and section2 are meant to be Strings, and the code is meant to be an array of strings.
Thanks for reading, I appreciate any help.
Gareth
You need to declare the function before you call it, otherwise the compiler will try to work out what the function prototype is on your behalf.
The message
previous implicit declaration of copyToCode
is telling you this. An implicit declaration is one that the compiler makes because you haven't yet given it an explicit declaration.
In your update to the question you say that code is intended to be an array of strings but you define it as:
char *code = malloc (32*1000* sizeof(char));
That allocates a single string. An array of strings would be held in a char**, just like argv. You would need to allocate the array first, which would contain n strings, each being a char*. Then you'd have to allocate each char* one by one in a loop.
This sort of coding is so much easier in C++ with the standard library string and vector classes.
This warning means that you have your declaration differs from the implementations. Or you have two different declarations of the function.
Because the warning is mentioning an implicit declaration it means that the declaration was deduced by gcc because you used the function before declaring it. GCC will use the parameters of the function call to deduce the declaration, which can lead to nasty problems.
If you declare the function, you will probably still get an error, but it should be much more specific.
Side note: If you are working with gcc, always compile as gcc -std=c99 -pedantic -Wall -Wextra -Wwrite-strings source.c
To your edit: Your variables are wrong. code is char * but your function takes char **.
Can you give us more detail about the variables you are using as parameters of the function?
Also worth to note is the fact that the first parameter of copyToCode is a pointer to a pointer of chars, what can be used as an array of strings.
The line:
void copyToCode (char **code, char *loc, char *data ){}
is the declaration of your function? In that case, you should write it as this:
void copyToCode (char **code, char *loc, char *data );
To define an array of strings, you'd need something like the following:
char **code;
int i;
code = malloc (32*sizeof(*code));
for (i=0; i<32; i++) {
code[i]=malloc(1000*sizeof(**code));
}
Note that this doesn't include error checking on the malloc results, which you should do. And it creates an array of size 32 containing strings of size 1000, which you shouldn't be hard coding.