I am starting to learn pointers in C.
Why do I have an error in line 8 at &i?
This is the source:
char * func(char *d, char *str)
{
return d;
}
int main(int argc, char *argv[])
{
char *i = NULL;
func(&i, "aaa"); // line 8. here I have the error (in "&i")
}
You are passing char** and the function expects a char*. You need to pass i without the address of & operator, because this way you are taking the address of the pointer.
Just pass func(i, "aaa");
The type of &i is not char * it is char * *.
You should go through this
How do pointer to pointers work in C?
When you write &i, you are passing the memory address of i, but since i is already a char*, the type of &i is char** (pointer to pointer to char). You therefore have an extraneous & that is causing a type mismatch. Simply remove the & and pass i to the function with taking its address:
func(i, aaa); //no need to use & on a variable that is already a pointer
As per your function prototype func(char *d, char *str) first parameter accepts pointer of char type and in char *i; i is a pointer of char type so pass it directly to function.
If you want to pass &i to the function you need to have pointer to pointer variable that is char **d.
Related
I have program
void alloc(char **p)
{
*p=(char*)malloc(sizeof(char)*3);
(*p)[0]='a';
(*p)[1]='f';
(*p)[2]='\0';
}
main()
{
char p[]="hrrgr";
alloc(&p);
printf("%s",p);
}
It prints nothing. Please explain this.
I know by passing char*p ; and alloc(&p) will do the trick. But the purpose of my question is to understand the output I am getting.
p is an array of 6 characters. Therefore p is of type char[6]. &p is then of type pointer to char[6] type, i.e., char (*)[6]. When you pass an array to a function, it evaluates to a pointer to its first element. Thus when you are passing a char (*)[6] type value to your alloc function, you are assigning a char (*)[6] type to a char ** type. They are incompatible types and have different pointer arithmetic. You can't make char (*)[6] behave like a char ** type, even by typecasting which will only suppress compiler warning.
This is not allowed: p is an array of 6 characters, not a char pointer. You should not treat &p as a char** pointer.
If you want to fix this code, declare p like char*, not as an array:
char *p="hrrgr";
alloc(&p);
Running demo on ideone.
&p is of type char (*)[6] but your function alloc expects an argument of type char **. You are passing wrong type parameter.
try this...
void alloc(char **p)
{
*p=(char*)malloc(sizeof(char)*3);
(*p)[0]='a';
(*p)[1]='f';
(*p)[2]='\0';
}
main()
{
char *p;
alloc(&p);
printf("%s",p);
}
When ever you declare an array then there will be continuous allocation of memory but here your are changing starting 3 elements memory which changes the whole location of array and printing garbage...
In the following code, once I remove the commented part which compares strings, I am getting a seg 11 fault. I am unable to understand why! Rest of the code is working fine. Any help is appreciated!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare_scores_desc(const void* scorea, const void* scoreb){
int a = *(int*)scorea;
int b = *(int*)scoreb;
return a-b;
}
int compare_names(const void* namea, const void* nameb){
char** a = *(char**)namea;
char** b = *(char**)nameb;
return strcmp(*a,*b);
}
int main(int argc, char* argv[]){
int scores[7] = {456,234,65,563,67,19,100};
int i;
qsort(scores,7,sizeof(int),compare_scores_desc);
puts("\nThese are the scores in order : \n");
for(i=0;i<7;i++)
printf("%i\n",scores[i]);
char *names[] = {"Krishna","Rama","Bhishma","Arjuna"};
/*qsort(names,4,sizeof(char*),compare_names);*/
puts("------------------");
puts("The names in order are : \n");
for(i=0;i<4;i++)
printf("%s\n",names[i]);
return 0;
}
In compare_names(), you are inappropriately dereferencing the arguments after the cast. The types for the local variables are type char **, but you are casting the arguments as char ** and dereferencing that results in a char *.
namea and nameb are pointers to the elements of your array names[] declared in main(). That means, their types are actually pointer to char *. When you dereferenced these arguments but assigned them to a char **, you cause the local variable to treat the char * as a char ** (your compiler should have issued a diagnostic warning you about this problem). Now, you take a pointer value that is a char *, and dereference it when you pass it to strcmp(). This causes the program to treat sizeof(char *) bytes of the string as a pointer value for the strcmp() function. Since 4 or 8 (or whatever sizeof(char *) is) bytes consisting of printable characters reinterpreted as a pointer value rarely yields a valid pointer, when strcmp() tries to use those pointers, a segmentation fault occurs.
One possible fix is to not dereference when you initialize your local variables. However, the arguments are const void *, so you can avoid the cast altogether if you declare your local variables to be a pointer to a const type:
int compare_names(const void* namea, const void* nameb){
char* const * a = namea;
char* const * b = nameb;
return strcmp(*a,*b);
}
Note that your implementation of compare_scores_desc() fails if a - b results in signed integer overflow. For example, if a is INT_MAX and b is -1. You should fix your implementation to work for all cases.
int compare_scores_desc(const void* scorea, const void* scoreb){
const int *a = scorea;
const int *b = scoreb;
return (*a > *b) - (*a < *b);
}
The problem is in your string comparison function, and here is probably the minimal way to fix it:
int compare_names(const void* namea, const void* nameb){
char* a = *(char**)namea;
char* b = *(char**)nameb;
return strcmp(a,b);
}
The namea and nameb arguments are pointers into the string vector. You understand this, which is why you used the char ** type.
However, all you have to do in the function is retrieve the char * pointers from that array. These char * pointers are already strings. You do not have to dereference them again; just pass them to strcmp.
Your original code has a constraint violation which requires a diagnostic. That should have tipped you off:
/* originally */
char** a = *(char**)namea; /* error: initialization from incompatible type */
You're dereferencing a char **, which produces char *, but you're storing that in a char ** again and dereferencing again, thereby wrongly treating the character data as a pointer.
I was testing an implementation of a comparator function. So here's my code that worked
#include <stdio.h>
#include <string.h>
int compare_names(const void* a, const void* b)
{
char* sa = (char*) a;
char* sb = (char*) b;
return strcmp(sa, sb);
}
int main()
{
char *a = "Bianca";
char *b = "Ana";
printf("Comparing %s with %s returns: %i\n", a, b, compare_names(a, b));
return 0;
}
But I don't think it's right as a and b arguments at compare_names function should turn out to be a pointer to a pointer of char. As pointed in a book I've read, the correct code for the compare_names function would be
int compare_names(const void* a, const void* b)
{
char** sa = (char**) a;
char** sb = (char**) b;
return strcmp(*sa, *sb);
}
But when I ran the code I got a segmentation fault (core dumped).
What am I missing here?
EDIT: I'm using gcc on Linux x64.
#include <stdio.h>
#include <string.h>
int compare_names(const void* a, const void* b)
{
char** sa = (char**) a;
char** sb = (char**) b;
return strcmp(*sa, *sb);
}
int main()
{
char *a = "Bianca";
char *b = "Ana";
printf("Comparing %s with %s returns: %i\n", a, b, compare_names(&a, &b));
return 0;
}
Now it's ok. You have to put the address of a and b in the printf parameters, since there are casted to char**.
char** sa = (char**) a; This line says: "If you direference twice your sa you will end up with a char" The problem is that since your a is a pointer to char you can not direference it twice. So the casting you are doing is generally wrong.
When casting, the compiler trys to interpret your *a which is a char as a pointer to char so when the conversion is performed your *sa ends up being a BadPtr since it fails to convert from char to char *.
So in your strcmp() you have two BadPtr.
You are passing char* arguments, not char** arguments. The example code you posted showing char** does the following:
1. Change generic pointer to a pointer to a string.
2. Compare the strings by dereferencing the char** arguments, meaning you're passing char* arguments to strcmp and return the result.
But you passed char* arguments to your comparison function, so the dereferencing ends up passing arguments of type char to strcmp. Since it expects pointers, the char is interpreted as a memory address. Comparing "hello" to "bye" actually compares the string at address 0x67 to the string at address 0x62, which will segfault.
Pass &a and &b to your comparison function to make it not segfault.
Both versions should work, however, for the second version of the "campare_names" function, you should pass an aditional pointer to each of the char pointers when calling the function.
However your version of the function is correct, it only makes sence to use double pointer parameters, when you are expecting that a function wil alter the pointer position or data being pointed. In this case, since the strcmp function only reads the char* data and doesn't make any changes to it, you don't need an aditional pointer.
If I have a function:
void myfunction(char** s);
Then I can pass a char* like this:
char* s = malloc(100);
myfunction(&s);
But my compiler won't allow me to do this:
char s[100] = {0};
myfunction(&s);
I thought that a pointer to the buffer should be allowed by the compiler.
Your function expects a pointer to a pointer (char **). You are trying to pass a pointer to an array instead (char (*)[100]). Why do you expect this to "be allowed by the compiler"? Arrays are not pointers. Arrays and pointers are objects of completely different nature. A pointer to a pointer is not in any way compatible with a pointer to an array. You can't use them interchangeably.
If you want you use your array-based buffer with a function that expects char **, you have to explicitly create a pointer to that buffer first
char s[100] = {0};
char *ps = s;
and then pass a pointer to that pointer, as you did before
myfunction(&ps);
#include <stdio.h>
#include <stdlib.h>
void myfunction(char* s[]){
printf("Here: %s",s);
}
int main(){
char s[100] = {'a'};
//char s[100] = "myString";
myfunction(&s);
}
This works on windows not sure on linux
I can't seem to understand the difference between the different declarations on an array or a 2d array.
for instance:
void swap(char **a, char **b) {
char *t = *a;
*a = *b;
*b = t;
}
int main(int argc, char **argv) {
char a[] = "asher";
char b[] = "saban";
swap(&a,&b);
}
this code doesn't compile, it outputs:
warning: passing argument 1 of ‘swap’ from incompatible pointer type
test.c:10: note: expected ‘char **’ but argument is of type ‘char (*)[6]’
isn't a a pointer to first cell of char array and &a is a pointer to pointer?
another example is:
char (*c)[3];
char (*d)[3];
swap(c,d);
doesn't compile either.. is char (*c)[3] same as a pointer to char a[] = "ab" ?
However this does compile:
char *c[3];
char *d[3];
swap(c,d);
so i'm totally confused. Why is there a difference? Is there some rules about that issue to prevent me from mistaking all the time?
Thank you all
I think that this is the source of your confusion.
An array variable is a fixed object. It refers to a fixed set of array members. It cannot be changed, although the values of the array members can.
In all expression contexts other than as the argument to unary & (address of) and sizeof an array will decay into a pointer to its first element.
Given:
char a[] = "asher";
The expression a will decay to a pointer to char (char*) and will point to the first character of a.
The expression &a is a pointer to an array of char (char (*)[]). It is a pointer to the complete array rather that a pointer to the first character. It is a different type to a pointer to the first character of the array although it will have the same value as a pointer to the first character of the array.
However, neither of the expressions a and &a are lvalues, they are temporary pointer values.
You cannot swap arrays, you can only swap pointers but to do this you need lvalue pointers whose address you can take.
void swap(char **a, char **b);
int main(int argc, char **argv) {
char a[] = "asher";
char b[] = "saban";
char* pa = a;
char* pb = b;
swap(&pa, &pb);
}