I'm trying to write my own version of strcat (I call it "append"). Here's what I have:
#include <stdio.h>
int main() {
char *start = "start";
char *add = "add";
append(start, add);
printf(start);
}
void append(char *start, char *add) {
//get to end of start word
char *temp = &start;
while (*temp != '\0') {
temp++;
}
*temp = *add;
while (*temp != '\0') {
*temp = *add;
}
}
When I compile, I get 3 warnings and an error:
1) warning: implicit declaration of function 'append' is invalid in C99
2) warning: format string is not a string literal (potentially insecure)
3) error: conflicting types for 'append'
I don't see how the arguments I pass into my append function within main conflict with the function definition below it.
4) warning: incompatible pointer types initializing 'char *' with an expression of type 'char **'; remove &
Why would I want to remove & here? I thought I could declare and initialize my char pointer to the proper memory address all at once.
Any help is much appreciated.
1) warning: implicit declaration of function 'append' is invalid in C99
and
3) error: conflicting types for 'append'
because, you did not provide a prototype of append() before using it. You need to add a forward declaration of a function before using it. Add
void append(char *start, char *add);
before main() or put the function definition before main()
Next, in case of
char *start = "start";
char *add = "add";
start and add are the pointers to string literals. They are usually placed in read-only memory, means you cannot change the contents. Any attempt to do so will lead to undefined behavior.
Then, regarding
2) warning: format string is not a string literal (potentially insecure)
printf(start);
is wrong usage in this case. You need to use it like
printf("%s\n", start);
Check the man page of printf() for more details.
and finally,
4) warning: incompatible pointer types initializing 'char *' with an expression of type 'char **'; remove &
is because of
char *temp = &start;
you need to use something like
char *temp = start; //start is a char *, no need for & here
Note: The recommended signature of main() is int main(void).
You have multiple problems with that short code. First you have
warning: implicit declaration of function 'append' is invalid in C99
The meaning of this warning is that you need to declare functions before you use them. If you don't declare a function before you use it, the compiler will have to guess its arguments and return type, and often it guesses badly.
Continuing with next warning:
warning: format string is not a string literal (potentially insecure)
This is because you provide a string variable to printf, this is, like the warning tells you, insecure. Think for example about a case where you read input from a user, and use that input as a format string to printf. What would stop the user from adding format codes in the input string? And since you don't pass arguments where would the arguments for those formats come from?
And now the error:
error: conflicting types for 'append'
This is because of the first problem, that the compiler guessed the arguments or return type of the function wrongly.
Now on to another major problem that doesn't show up as compiler errors or warnings, namely undefined behavior.
The problem is that your start and add variables points to string literals. String literal are read only (in fact, a string literal is a pointer to an array of non-modifiable characters). The first problem is that you try to modify the contents of these arrays, the second is that the arrays are only as big as needed and you are writing outside of that memory. Both these problems are causes for undefined behavior.
But this is the easy part, what the compiler could detect.
The worse part is that as you declared start as char *start = "start", it only points to an array of 6 chars (5 letters + terminating null).
So when you try to add add at its end you get Undefined Behaviour (for writing beyond and of an array) ! In that case you are writing memory where any else could be => your program is likely to break or segfault.
C99 is pretty restrictive in how it wants you to declare things.
As Sourav said,
1 & 3 are caused by your append() function is used before it's declared in the file, which causes the compiler to generate an implicit declaration for you. Move the append() function above main() to fix that (or add a function prototype).
4 is caused by this line: char *temp = &start;
temp here is actually a char**, since you're taking the address of a char*
Related
Let's say you have a function taking a string as an argument:
void foo(char *arg);
If we know for certain that the array (not to be confused with string length, thanks chux) will always have a certain size, let's say 8, then we can instead do:
void bar(char (*arg)[8]);
and then call it like this:
char str[8] = "Hello";
bar(&str);
We need to add the & for this to work properly, but the above code will emit a warning if you pass an array of wrong size or type, which is exactly what I want to achieve. But we will obviously need to modify the body a bit. So my question is simply if this wrapper technique would work:
void bar(char (*arg)[8]) {
char *tmp = (char*) arg;
foo(tmp);
}
What I'm trying to achieve here is that warnings should be emitted if called with an array of wrong size. Is the above solution safe? Is it safe to cast pointer to array of char to pointer to char?
I tried it, and it works, and emits no warnings with -Wall -Wextra -pedantic. And as soon as I change the size of str I get:
<source>: In function 'main':
<source>:18:9: warning: passing argument 1 of 'bar' from incompatible pointer type [-Wincompatible-pointer-types]
18 | bar(&str);
| ^~~~
| |
| char (*)[9]
<source>:9:17: note: expected 'char (*)[8]' but argument is of type 'char (*)[9]'
9 | void bar(char (*arg)[8]) {
| ~~~~~~~^~~~~~~
which is exactly what I want. But is it safe, or is it UB? I would like to do this, not only via a wrapper, but also by rewriting the original function, like
void foo(char (*argaux)[8]) {
char *arg = *argaux;
// Copy body of original foo
I know that I can achieve basically the same thing using structs, but I wanted to avoid that.
Runnable code: https://godbolt.org/z/GnaP5ceMr
char *tmp = (char*) arg; is wrong, these are not compatible pointer types. You can fix this easily though:
char *tmp = *arg;
*arg gives a char[8] which then decays into a pointer to its first element. This is safe and well-defined. And yes, pointers have much stronger "typing" in C than pass-by-value, so the compiler will recognize if an array of wrong size is passed.
Please note however that this leads to other problems: you can no longer have const correctness.
See Const correctness for array pointers?
This is not safe:
char *tmp = (char*) arg;
Because you're attempting to convert a char (*)[8] to a char *. While you might get away with it since a pointer to an array will (at least on x86-64) have the same numeric value as a pointer to the first member of an array, the standard doesn't guarantee that it will work. You would first need to dereference the parameter:
char *tmp = *arg;
In theory you should be able to do this:
void foo(char arg[static 8]);
This means that arg must be an array of at least that size.
The description of this syntax is in section 6.7.6.3p7 of the C standard:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted
to ‘‘qualified pointer to type’’, where the type qualifiers (if
any) are those specified within the [ and ] of the array
type derivation. If the keyword static also appears within the
[ and ] of the array type derivation, then for each call
to the function, the value of the corresponding actual argument
shall provide access to the first element of an array with at least as
many elements as specified by the size expression.
However, most implementations don't enforce this restriction and it doesn't prevent you from passing an array larger than expected.
I was learning C and i wanna to print a name using function, so i made this code:
#include <stdio.h>
void pn(char x);
void main()
{
pn("HHH");
}
void pn(char x)
{
printf("Hello %s\n",x);
}
and the output is nothing , so i change the argument and make it as pointer and then it works:
void pn(char* x);
void main()
{
pn("HHH");
}
void pn(char* x)
{
printf("Hello %s\n",x);
}
the output is : Hello HHH
as i know, pointer is to store address for a variable , but here i dont send any address ? so why it works only when i put a pointer as argument ?
In the first program the function parameter
void pn(char x);
has the type char that is the function expects as an argument a single character.
However the function is called with the string literal "HHH" as its argument
pn("HHH");
String literals in C have types of character arrays. For example the string literal "HHH" has the type char[4] because the string literal includes also an invisible terminating zero character.
Used in expressions as for example as function arguments arrays are implicitly converted to pointers to their first elements. So the string literal "HHH" is converted to a temporary object of the type char * that points to the first character 'H'..
As a result the function has undefined behavior due to the incompatibility of the parameter and the corresponding argument.
In the second program the function parameter was correctly changed to the type char * and the program produced the expected result.
If you wanted to output just one character in the first program then it should be changed at least the following way
#include <stdio.h>
void pn(char x);
void main()
{
pn("HHH"[0]);
}
void pn(char x)
{
printf("Hello %c\n",x);
}
Or it would be more clear to call the function like
pn( 'H' );
passing a character literal.
A string in C is defined as an array of characters terminated with a null character. Arrays in C are defined by a pointer that points to the first element in the array.
Your function doesn't work because its parameter only accepts a single character as an argument, not a pointer to an array of characters.
"So why it works only when I put a pointer as argument?"
"HHH" is a string literal of type array of 4 char (char [4]).
When you use a string literal as function argument in C, the string literal will be evaluated to a pointer to the first element of the char array consisting the string, type pointer to char (char *).
In this case, "HHH" will gain a pointer to the memory address of the first char element which contains the first H.
If you now use x as parameter of type char, there is:
A type mismatch at calling the function pn between argument and parameter - char * vs. char, which invokes undefined behavior, and
Also a type mismatch at printf("Hello %s\n",x); as the %s conversion specifier expects an argument of type pointer to char. This also invokes undefined behavior since you provided an argument of type char, which is not allowed per the C standard.
"If a conversion specification is invalid, the behavior is undefined. 288) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."
"288) See "future library directions" (7.31.11)."
Source: C18, 7.21.6.1/9 - "The fprintf function"
Side notes:
Usually, a compiler should warn you about both type mismatches by default (without any additional flags). So you either seem to ignore the compiler warnings or don't got an appropriate compiler. Switch the compiler to GCC or Clang (if not done yet) and never ignore compiler warnings:
Why should I always enable compiler warnings?
I'm working on a block-cipher program in C, and it seems none of my functions are returning the proper type of pointer, so my code won't even compile.
An example is this:
char *evenString(char * inText) /*takes a string of text. If it has an odd number of chars, it adds ASCII char 19 as padding.*/
{
int inputLength = strlen(inText);
char* evenText; /*pointer to character array*/
if(inputLength%2) /*If even, pad*/
{
evenText = (char*) malloc(sizeof(char) * (inputLength+2));
strcpy(evenText,inText);
evenText[inputLength] = FILLER_CHARACTER;
}
else
{
evenText = (char*) malloc(sizeof(char) * (inputLength+1));
strcpy(evenText,inText);
}
return evenText; /*which should be a char*, right?*/
}
When I call it in main, the call looks like this:
char *plainText = evenString(inputText);
And the compiler brings up these exceptions:
block_cypher.c:28:22: warning: initialization makes pointer from integer without a cast [enabled by default]
char *plainText = evenString(inputText);
block_cypher.c: At top level:
block_cypher.c:38:7: error: conflicting types for ‘evenString’
char *evenString(char * inText)
block_cypher.c:28:22: note: previous implicit declaration of ‘evenString’ was here
char *plainText = evenString(inputText);
block_cypher.c: In function ‘evenString’:
block_cypher.c:45:26: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
evenText = (char*) malloc(sizeof(char) * (inputLength+2));
block_cypher.c:53:26: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
evenText = (char*) malloc(sizeof(char) * (inputLength+1));
It's like this for all my functions, which all return a pointer of type int* or char*. I haven't even been able to compile, so I don't even know where to begin to fix this. Any insight to this would be greatly appreciated. Thank you for your time!
UPDATE: Thank you all! I followed all your advice:
I inserted function prototypes, which resolved many of the errors right away.
I included ; I don't know what I was thinking, but I've been working in a Java IDE a lot recently and forgot a lot of important stuff about C.
As suggested, I stopped casting malloc(). My C professor told us that since malloc returns a VOID* pointer, it's always good practice to cast it.
Thank you all!
The below error:
block_cypher.c:28:22: note: previous implicit declaration of ‘evenString’ was here
// ^^^^^^^^
char *plainText = evenString(inputText);
indicates that at the location where the evenString function is invoked, the compiler doesn't know that function's signature, which usually means you are missing proper header file. As a result of that, the compiler deduces its own declaration (based on supplied arguments), with return type defaulted to int:
int evenString(char* inText);
And this is where the error originates from:
warning: initialization makes pointer from integer without a cast [enabled by default]
// ^^^^ ^^^^^^^
After reading all errors / warnings it looks that you define the evenString function after its first usage. You should either declare the function before its first usage, or move entire function definition to the top of your translation unit (at least before you use that function for the first time).
That is:
block_cypher.c: At top level:
block_cypher.c:38:7: error: conflicting types for ‘evenString’
// ^^ ^^^^^^^^^^^
char *evenString(char * inText)
block_cypher.c:28:22: note: previous implicit declaration of ‘evenString’ was here
// ^^
char *plainText = evenString(inputText);
means you have invoked evenString(inputText); at line 28, where the compiler deduced the return type to be int, and only then you declare/define the function at line 38, which is too late (at least without previous declaration).
To make it working, try adding declaration before that 28 th line:
char* evenString(char* inText);
You need to add a prototype for evenString() before calling the function.
Without a prototype the compiler assumes it returns a value of type int and the assignment
char *plainText = evenString(inputText);
is like
char *plainText = <int value here>;
so the compiler complains
i have a token of type char* that i want to get the second element of. For example the tokens all consist of a 4 long course number like 1405, i want to grab the 2nd character or 4, and turn it into an interger. I use strcpy to add the string into a char string array in order to grab the 4 but i get some errors and im not quite sure how to fix them. here is my code
#include <stdio.h>
#include <string.h>
int search(struct id array[],char* tok);
int main(void)
{
char* token2 = "1405";
char* text[] = malloc(strlen(token2)+1);
strcpy(text,token2);
int i,number;
for(i=0;i<4;i++)
if(i==1)
{
number = atoi(token2[i]); }
printf("%d\n",number);
i get the following compilation errors
gcc structures_arrayof.c
structures_arrayof.c: In function ‘main’:
structures_arrayof.c:23:20: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
char* text[] = malloc(strlen(token2)+1);
^
structures_arrayof.c:23:5: error: invalid initializer
char* text[] = malloc(strlen(token2)+1);
^
structures_arrayof.c:24:5: warning: passing argument 1 of ‘strcpy’ from incompatible pointer type [enabled by default]
strcpy(text,token2);
^
In file included from structures_arrayof.c:5:0:
/usr/include/string.h:129:14: note: expected ‘char * __restrict__’ but argument is of type ‘char **’
extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
atoi is for converting a string to integer. However, you want to convert a single character.
You can write:
number = token2[i] - '0';
This works because the digits '0' through '9' have consecutive character codes. There is no need to malloc or whatever.
To work with what you are doing....
This should clear up the first set of errors.
char* text = malloc(strlen(token2)+1);
You are need a pointer to a char string.
To do the the conversion, you need to null terminate the string.
token2[i+1]=0;
Then you can
number = atoi(token2[i]);
The first error:
You forgot to #include <stdlib.h> to get malloc.
The second error:
A string represented as a pointer to a char, so you just need
char *text = malloc(strlen(token2)+1);
Drop the brackets.
Alternatively, since size is known at compile time, you can declare it without malloc using char text[5];
The third error:
It warns you that you are using the wrong type of pointer. This should be fixed by changing the declaration as mentioned above.
The fourth error (not in your pasted compiler output):
As Matt McNabb pointed out, you should not be using atoi. Replace it with
number = token2[i] - '0'; This will subtract the ASCII vaule of 0, returning the integer value of the character.
An additional comment: I'd recommend changing the spacing/indent style of the if statement. Putting an opening brace on its own line and a closing brace on the same line as the last statement is very non-conventional, and it decreases readability.
I suggest you pick a standard style and try to stick to it. Personally, I like the Linux Kernel Coding Style, but feel free to choose whichever suits you.
I am trying to build the M-SIM architecture simulator, but when I run the make utility, gcc reports this error (it is not even a warning)
note: expected 'char *' but argument is of type 'const char *'
Since when this is considered an error. Is there any flags that can bypass this check?
This is an error because passing a const char* argument to a function that takes a char* parameter violates const-correctness; it would allow you to modify a const object, which would defeat the whole purpose of const.
For example, this C program:
#include <stdio.h>
void func(char *s) {
puts(s);
s[0] = 'J';
}
int main(void) {
const char message[] = "Hello";
func(message);
puts(message);
return 0;
}
produces the following compile-time diagnostics from gcc:
c.c: In function ‘main’:
c.c:10:5: warning: passing argument 1 of ‘func’ discards qualifiers from pointer target type
c.c:3:6: note: expected ‘char *’ but argument is of type ‘const char *’
The final message is marked as a "note" because it refers to the (perfectly legal) declaration of func(), explaining that that's the parameter declaration to which the warning refers.
As far as the C standard is concerned, this is a constraint violation, which means that a compiler could treat it as a fatal error. gcc, by default, just warns about it and does an implicit conversion from const char* to char*.
When I run the program, the output is:
Hello
Jello
which shows that, even though I declared message as const, the function was able to modify it.
Since gcc didn't treat this as a fatal error, there's no need to suppress either of the diagnostic messages. It's entirely possible that the code will work anyway (say, if the function doesn't happen to modify anything). But warnings exist for a reason, and you or the maintainers of the M-SIM architecture simulator should probably take a look at this.
(Passing a string literal to func() wouldn't trigger these diagnostics, since C doesn't treat string literals as const. (It does make the behavior of attempting to modify a string literal undefined.) This is for historical reasons. gcc does have an option, -Wwrite-strings, that causes it to treat string literals as const; this actually violates the C standard, but it can be a useful check.)
As I mentioned in a comment, it would be helpful if you'd show us the code that triggers the diagnostics.
I even downloaded and built the M-SIM architecture simulator myself, but I didn't see that particular message.
Pointers to const-qualified types do not implicitly convert to pointers to non-const-qualified types. An explicit conversion via a cast is necessary, for example:
foo((char *)bar)
First in a function call (of a function defined with a prototype), the arguments are converted to the type of the parameters as if by assignment.
You can assign a value of type char * to an object of type const char * but you cannot assign a const char * value to a char * object.
This constraint appears in the constraints of assignment operator:
(C99, 6.5.16.1p1) "One of the following shall hold: [...] - both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;"
This constraint permits the first assignment but disallows the second.
Declaring a pointer with type const char * means you won't modify the object pointed to by the pointer. So you can assign the pointer a value of char * type, it just means the object won't be modified through the const char * pointer.
But declaring a pointer of type char * means you could modify the object pointed to by the pointer. It would not make sense to assign it a value of const char *.
Remember that in C, const does not mean constant but rather read-only. The const qualifier put before pointer types means you promise not to modify objects through objects of these pointers types.
Take these steps if you haven't already:
Declare a char pointer.
If necessary, allocate space and copy contents from the constant string. (e.g. by
using strdup())
And substitute the constant char pointer with the new char pointer.