This question already has answers here:
What Does Adding One to a Character Array in C Do?
(4 answers)
Closed 9 years ago.
If I have a pointer to a pointer like the variable 'bs' in this sample:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char **bs = {"this", "is", "a", "test"};
puts(bs);
return 0;
}
the puts statement shows 'this' on the screen. How would I go about getting it to show 'is' and so forth. I though I could just do something like:
puts(bs+1);
What does adding a number to a pointer variable actually do? Does it point to the next address, or does it take the type of the pointer into consideration and uses the size of the variable it is pointing to to determine where the next variable starts in memory?
the puts statement shows 'this' on the screen.
That's just (bad) luck, the code invokes undefined behavior, if the compiler doesn't refuse to compile it at all.
The warnings produced by clang (by default) for the code are
$ clang badpoint.c
badpoint.c:6:18: warning: incompatible pointer types initializing 'char **' with an
expression of type 'char [5]' [-Wincompatible-pointer-types]
char **bs = {"this", "is", "a", "test"};
^~~~~~
badpoint.c:6:26: warning: excess elements in scalar initializer
char **bs = {"this", "is", "a", "test"};
^~~~
badpoint.c:8:10: warning: incompatible pointer types passing 'char **' to parameter of
type 'const char *'dereference with * [-Wincompatible-pointer-types]
puts(bs);
^~
*
/usr/include/stdio.h:688:32: note: passing argument to parameter '__s' here
extern int puts (__const char *__s);
^
3 warnings generated.
gcc generates a warning for each excess initializer, while clang gives only one for the first of the three, otherwise the warnings from gcc (also by default) are a bit less informative, but the same.
So what's going on?
You are trying to initialize a scalar (char**), and provide {"this", "is", "a", "test"}, a brace-enclosed initializer-list containing four _initializer_s. Per 6.7.9 (11)
The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.
That violates a "shall" requirement, and hence invokes undefined behavior.
If the compiler does not terminate the translation there, it probably just ignores the excess initializers - clang and gcc both do - and treats the declaration as if it was
char **bs = "this";
Which causes the warning about initialization from an incompatible type, since that tries to initialize a char** with a char[5], but per 6.5.16.1 the type of the initializer must be compatible with char**.
Nevertheless, if the compiler successfully translates the program, it is likely to result in bs containing the address of the first char in the string literal (the array "this" is converted to a pointer to its first element on the right hand side of an assignment operator).
Then the call
puts(bs)
Passes a pointer of the wrong type (char**) to puts, which is a constraint violation and requires a diagnostic message from the compiler (and makes the program invalid).
But the pointer happens to contain the correct address, and the undefined behavior manifests as the program printing "this".
How would I go about getting it to show 'is' and so forth
By correcting the program. As is, the strings "is", "a", and "test" do not even appear in the object file produced by gcc or clang.
One way to correct it would be changing the declaration to
char *bs[] = {"this", "is", "a", "test"};
So that bs is an array of four char*, pointing to the (respective first elements of the) four strings.
Then you have to adjust the call to puts, dereferencing or subscripting bs,
puts(bs[0]);
To print "this";
for(int i = 0; i < 4; ++i) {
puts(bs[i]);
}
To print all four strings on separate lines.
Related
While reviewing my code I realized I had placed an extra & while passing a char array to strcpy and missed the resulting warning; regardless, everything worked as expected. I then reproduced the behavior in this example:
#include <string.h>
#include <stdio.h>
void main() {
char test1[32] = {0};
char test2[32] = {0};
strcpy(test1, "Test 1\n");
strcpy(&test2, "Test 2\n");
printf(test1);
printf(test2);
printf("%i %i\n", test2, &test2);
}
Here I copy a string to the address of test2 and the compiler complains accordingly:
main.c: In function ‘main’:
main.c:9:12: warning: passing argument 1 of ‘strcpy’ from incompatible pointer type [-Wincompatible-pointer-types]
9 | strcpy(&test2, "Test 2\n");
| ^~~~~~
| |
| char (*)[32]
In file included from main.c:1:
/usr/include/string.h:125:39: note: expected ‘char * restrict’ but argument is of type ‘char (*)[32]’
125 | extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
| ~~~~~~~~~~~~~~~~~^~~~~~
However the code is still compiled and the result seems to ignore the second level of indirection. Even when printing the address &test2 it is the same as simply test2.
./a.out
Test 1
Test 2
-1990876288 -1990876288
I must admit this is a part of the C language that completely escapes me. Why is the & operand seemingly ignored when targeting an array?
The first byte of the first element in the array is at the same place as the first byte of the array, because they are the same byte.
Most C implementations use the memory address, or some representation of it, of the first byte of an object as a pointer to the object. The array contains its elements, and there is no padding: The first element of the array starts where the array starts. So the first byte in the first element is the first byte in the array. So they have the same memory address.
There is a rule in C that converting a pointer to an object to a char * produces a pointer to the first byte of an object (C 2018 6.3.2.3 7). So, given an array a, (char *) &a[0] is a pointer to the first byte of the first element, and (char *) &a is a pointer to the first byte of the array. These are the same byte, so (char *) &a[0] == (char *) &a.
However, &a[0] and &a have different types. If you attempt to compare them directly with &a[0] == &a, the compiler should issue a warning that the types do not match.
If you pass &a as an argument to a routine that expects &a[0], it will often work in most modern C implementations because they use plain memory addresses as pointers, so &a is represented with the same bits (a memory address) as &a[0], so the receiving routine gets the value it expected even though you passed a pointer of the wrong type. However, the behavior of your program will not be defined by the C standard, since you have violated the rules. This was more of a problem in older C implementations when memory models were not simple flat address spaces, and different types of pointers may have had different representations.
Your code works in this case because, once the pointer argument (with or without the extra &) gets to the strcpy function, it is interpreted as a simple char* value. Thus, any pointer arithmetic (such as the likely increments) performed in that function will be correct.
However, there are cases where using a pointer-to-char and pointer-to-array-of-char will yield significantly different results. Pointer arithmetic is such a case: if p is a char* variable, then ++p will add the size of a char (i.e. 1) to the address stored in p; however, if q is an array of char* pointers, then ++q will add the size of a pointer to the address in q. And, if r is the address of an array of character strings, then ++r will add the size of the entire array to the address stored in r.
So, it's good that the compiler warns you about that extra &. Be very careful about addressing (no pun intended) such issues, if ever your compiler warns you about them.
Why is the & operand seemingly ignored when targeting an array?
The conversion of char (*)[32] to char * is UB.
Is is not ignored by the compiler, hence the warning.
The compiler emitted code did convert the pointer from one type to the other in a common fashion resulting in acceptable behavior. Still remains UB.
Best if the programmer does not ignore the warning.
This is the following code:
Why it is giving segmentation fault when I try to access first value of array?
What are all this warnings?
#include<stdio.h>
int main(void)
{
int *ptr = {1,2,3,4,5};//Is it not similar to char *ptr="Stackoverflow"?
printf("%d\n",*ptr);// why Segmentation fault(core dumped) instead of 1
return 0;
}
...
output:
warning: initialization makes pointer from integer without a cast [enabled by default]
int *ptr = {1,2,3,4,5};
^
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]
//Is it not similar to char *ptr="Stackoverflow"?
TL;DR No, it is not.
The used initializer, {1,2,3,4,5} is called a brace-enclosed initalizer which is supposed to initialize the values of the type of the elements. This is used for aggregate or union type type, like mentioned as in C11, chapter §6.7.9, Initialization
the initializer for an object that has aggregate or union type shall be a brace enclosed
list of initializers for the elements or named members.
Here, the initializer list contains all int values, and you're trying to initialize a pointer thought it. This is wrong.
Also, regarding the scalar type, quoting C11, chapter §6.2.5
Arithmetic types and pointer types are collectively called scalar types.[...]
and the aggregate types
[...]Array and
structure types are collectively called aggregate types.
There are many issues here, like
You're using int value to initialize an int *.
You're ending up supplying a brace enclosed list containing more than one initializer element for a scalar object.
So, later in your code,
printf("%d\n",*ptr);
is essentially an invalid memory access, which invokes undefined behavior. The segmentation fault is one of the many side-effects.
Coming to the point of the comment,
char *ptr="Stackoverflow"?
In case of char *ptr="Stackoverflow";, here, "Stackoverflow" is called a string literal and ptr is initalized with the base address of the string literal.
Solution:
You need to have an array of ints which you can initialize using the brace-enclosed initializer. Something along the line of
int ptr[] = {1,2,3,4,5};
will be valid. Then you can use it like
for(int i = 0; i < 5; i++)
printf("%d\t", *(ptr+i));
Your original code is invalid. It contains at least two constraint violations: it provides initializers for objects that don't exist, and it tries to use an initializer 1 (of type int) for an int* object. A compiler could (and IMHO should) simply reject it. gcc is being overly permissive by compiling your code after merely warning about the errors. The resulting code has undefined behavior.
const char *cptr = "Hello";
The above is valid. "Hello" is an expression of array type (specifically of type char[6]). In most contexts, including this one, such an expression is implicitly converted to a pointer to the array's 0th element. Note that I've added const so the compiler will at least warn if I attempt to modify the data that cptr points to.
int *iptr = { 1, 2, 3, 4, 5 }; // invalid
This is invalid. You might expect that it's handled similarly to cptr. The problem is that { 1, 2, 3, 4, 5 } is not an expression; it's valid only in an initializer. It could be a valid initializer for an array object, but since it's not an expression, the array-to-pointer conversion rule doesn't apply.
Assuming your compiler supports C99 or later (specifically the compound literal feature), you can write:
int *iptr = (int[]){ 1, 2, 3, 4, 5 };
(This is not a cast; the syntax is similar, but the { ... } is not an expression.)
The compound literal is an expression of array type, specifically int[5], and the array-to-pointer conversion applies.
One caveat: A string literal creates an array object, and that object has static storage duration, meaning that it exists throughout the execution of the program. A compound literal creates an object with static storage duration only if it appears outside any function; inside a function, it creates an object with automatic storage duration, meaning that it ceases to exist when you reach the end of the current block. In this case, it's defined inside the main function, so it's not likely to be an issue. But it's something to watch out for. For example, this is safe:
const char *new_string(void) {
const char *result = "hello";
return result;
}
but this is not:
int *new_array(void) {
int *result = (int[]){ 1, 2, 3, 4, 5 };
return result; /* BAD! */
}
because the array ceases to exist when you leave the function. To avoid that, you can create the array object explicitly to make it static:
int *new_array(void) {
static const int arr[] = { 1, 2, 3, 4, 5 };
int *result = arr; /* or &arr[0] */
return result; /* or "return arr;" */
}
basically that line is invalid, the c compiler has tried to make sense of it but really cannot. You need the syntax here
How to initialize all members of an array to the same value?
Summary
int myArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
try:
#include<stdio.h>
int main(void)
{
int ptr[] = {1,2,3,4,5};//Is it not similar to char *ptr="Stackoverflow"?
printf("%d\n",*ptr);// why Segmentation fault(core dumped) instead of 1
return 0;
}
In the original code:
int *ptr = {1,2,3,4,5};
{1,2,3,4,5} won't initialize an integer array. String initialization is a special case which is not applicable over other types.
So this code will initialize an integer pointer which will point to memoery address 0x00000001 (first one in the initializer block)
This address is out of program scope and thus segmentation error came into picture.
Pointer is a scalar data type and standard says that (C11-6.7.9):
The initializer for a scalar shall be a single expression, optionally enclosed in braces.
You can't initialize a scalar data type with brace enclosed initializer having more than one expressions.
In case of
char *ptr="Stackoverflow";
ptr is pointing to the object with type array of char (look at standard C11:§6.7.9/11). It just initializes ptr to the start of the address of string literal.
You are trying to store values in an unitialised pointer. The value of the pointer must be attached to a memory location prior you can access it.
Just do some malloc before:
int $ptr;
ptr = malloc( 5* sizeof(int)); // since you have 5 values and then you can initialize your data
for (i=0;i<5;i++) {
(*ptr+i) = (i+1);
}
I am learning C and I am having some troubles to truly understand arrays and pointers in C, after a lot of research (reading the K&R book (still reading it), reading some articles and some answers here on SO) and taking tons of notes along the way, I've decided to make a little test / program to prove my understanding and to clear some doubts, but I'm yet confused by the warning I am getting using this code:
#include <stdio.h>
int main(int argc, char **argv) {
char a[3] = {1, 2, 3};
char *p = &a;
printf("%p\n", a); /* Memory address of the first element of the array */
printf("%p\n", &a); /* Memory address of the first element of the array */
printf("%d\n", *a); /* 1 */
}
Output compiling with GNU GCC v4.8.3:
warning: initialization from incompatible pointer type
char *p = &a;
^
0x7fffe526d090
0x7fffe526d090
1
Output compiling with VC++:
warning C4047: 'initializing' : 'char *' differs in levels of indirection from 'char (*)[3]'
00000000002EFBE0
00000000002EFBE0
1
Why am I getting this warning if &a and a are supposed to have the same value and compiling without warnings when using just a as an initializer for p?
&a is the memory address of the entire array. It has the same value as the address of the first element (since the whole array starts with the first element), but its type is a "pointer to array" and not "pointer to element". Hence the warning.
You are trying to initialize a char* with a pointer to a char[3], which means a char(*)[3].
Unsurprisingly, the compiler complains about the mismatch.
What you wanted to do is take advantage of array-decay (an array-name decays in most contexts to a pointer to its first element):
char* p = a;
As an aside, %p is only for void*, though that mismatch, despite being officially Undefined Behavior, is probably harmless.
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.
Why is it that I can assign a string to a variable of type int? Eg. the following code compiles correctly:
int main(int argv, char** argc){
int a="Hello World";
printf(a);
}
Also, the program doesn't compile when I assign a string to a variable of a different type, namely double and char.
I suppose that what is actually going on is that the compiler executes int* a = "Hello World"; and when I write double a="Hello World";, it executes that line of code as it is.
Is this correct?
In fact, that assignment is a constraint violation, requiring a diagnostic (possibly just a warning) from any conforming C implementation. The C language standard does not define the behavior of the program.
EDIT : The constraint is in section 6.5.16.1 of the C99 standard, which describes the allowed operands for a simple assignment. The older C90 standard has essentially the same rules. Pre-ANSI C (as described in K&R1, published in 1978) did allow this particular kind of implicit conversion, but it's been invalid since the 1989 version of the language.
What probably happens if it does compile is that
int a="Hello World";
is treated as it if were
int a = (int)"Hello World";
The cast takes a pointer value and converts to int. The meaning of such a conversion is implementation-defined; if char* and int are of different sizes, it can lose information.
Some conversions may be done explicitly, for example between different arithmetic types. This one may not.
Your compiler should complain about this. Crank up the warning levels until it does. (Tell us what compiler you're using, and we can tell you how to do that.)
EDIT :
The printf call:
printf(a);
has undefined behavior in C90, and is a constraint violation, requiring a diagnostic, in C99, because you're calling a variadic function with no visible prototype. If you want to call printf, you must have a
#include <stdio.h>
(In some circumstances, the compiler won't tell you abut this, but it's still incorrect.) And given a visible declaration, since printf's first parameter is of type char* and you're passing it an int.
And if your compiler doesn't complain about
double a="Hello World";
you should get a better compiler. That (probably) tries to convert a pointer value to type double, which doesn't make any sense at all.
"Hello World" is an array of characters ending with char '\0'
When you assign its value to an int a, you assign the address of the first character in the array to a. GCC is trying to be kind with you.
When you print it, then it goes to where a points and prints all the characters until it reaches char '\0'.
It will compile because (on a 32-bit system) int, int *, and char * all correspond to 32-bit registers -- but double is 64-bits and char is 8-bits.
When compiled, I get the following warnings:
[11:40pm][wlynch#wlynch /tmp] gcc -Wall foo.c -o foo
foo.c: In function ‘main’:
foo.c:4: warning: initialization makes integer from pointer without a cast
foo.c:5: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast
foo.c:5: warning: format not a string literal and no format arguments
foo.c:5: warning: format not a string literal and no format arguments
foo.c:6: warning: control reaches end of non-void function
As you can see from the warnings, "Hello World" is a pointer, and it is being converted to an integer automatically.
The code you've given will not always work correctly though. A pointer is sometimes larger than an int. If it is, you could get a truncated pointer, and then a very odd fault when you attempt to use that value.
It produces a warning on compilers like GCC and clang.
warning: initialization makes integer from pointer without a cast [enabled by default]
The string literal "Hello World" is a const char *, so you are assigning a pointer to an int (i.e. casting the address of the first character in the string as an int value). On my compilers, gcc 4.6.2 and clang-mac-lion, assigning the string to int, unsigned long long, or char all produce warnings, not errors.
This is not behavior to rely on, quite frankly. Not to mention, your printf(a); is also a dangerous use of printf.