So far as I know, when we create an array or string, their names are pointers to their first elements. And the pointer, in this case, cannot be changed.
However, this piece of code works:
#include <stdio.h>
void swapString(char **str1_ptr, char **str2_ptr)
{
char *temp = *str1_ptr;
*str1_ptr = *str2_ptr;
*str2_ptr = temp;
}
int main()
{
char strings[][30] = {"Test1","Test2","Test3"};
int i;
swapString((char **)&strings[0],(char **)&strings[1]);
for(i=0;i<3;i++){
printf("%s\n",strings[i]);
}
return 0;
}
Why does this work well?
Is it a good practice to swap strings (or arrays in general) in this way?
When it is not a good idea to use this approach?
Why does this work well?
It does not work well.
It seems to work well because you lie to your compiler and use insufficient test data.
void swapString(char **str1_ptr, char **str2_ptr)
{
char *temp = *str1_ptr;
*str1_ptr = *str2_ptr;
*str2_ptr = temp;
}
You claim to provide a pointer to a pointer. As a consequence this functions swaps as much bytes between two places as a pointer takes. That is 4 or 8 bytes on most systems.
But then you pass addresses of arrays, not pointers to pointers:
char strings[][30] = {"Test1","Test2","Test3"};
swapString((char **)&strings[0],(char **)&strings[1]);
This is causing undefined behaviour. You are using casts because your compiler told you that this is crab if you remove them:
test.c:15:28: warning: passing argument 2 of ‘swapString’ from incompatible pointer type [-Wincompatible-pointer-types]
15 | swapString(&strings[0],&strings[1]);
| ^~~~~~~~~~~
| |
| char (*)[30]
test.c:3:41: note: expected ‘char **’ but argument is of type ‘char (*)[30]’
As a result the function thinks it swaps a pointer but instead it swaps the first 8 bytes of your array.
To verify this you could use longer strings:
#include <stdio.h>
void swapString(char **str1_ptr, char **str2_ptr)
{
char *temp = *str1_ptr;
*str1_ptr = *str2_ptr;
*str2_ptr = temp;
}
int main()
{
char strings[][30] = {"Test111111","Test222222","Test333333"};
int i;
swapString((char **)&strings[0],(char **)&strings[1]);
for(i=0;i<3;i++){
printf("%s\n",strings[i]);
}
return 0;
}
And then you get the result you deserve. ;)
Test$ ./test
Test222211
Test111122
Test333333
In case of 32bit addresses you need to make the first 4 bytes of your strings distinguishable.
As you can see, there are no swapped pointers at all. Just the start of your arrays is messed up.
If you want to swap strings by just swapping pointers, you need to change your array definition:
char *strings[] = {"Test111111","Test222222","Test333333"};
swapString(&strings[0],&strings[1]);
As a side effect you can also drop the casts from your function call as the types now match what is expected.
Related
I have this to sort a string array (argv itself) in two ways with qsort. i started with the linux man 3 qsort example. There cmpstringp is only one line:
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(*(const char **) p1, *(const char **) p2);
I tried to reformulate this and it compiles without warnings only with
char *const* sp1 = vep1
i.e. const belongs in the middle - it is the argv entry (?). At least that is what qsort declaration demands. The above cast from man page seems different and complicated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* vep: void pointer to elements being qsorted
sp: string pointer (char **) */
int
cmpstringp(const void *vep1, const void *vep2) {
char *const *sp1 = vep1,
*const *sp2 = vep2;
return strcmp(*sp1, *sp2);
}
/* Make char* from void*, and even char */
int
cmpchar(const void *p1, const void *p2) {
const char *cp1 = p1,
*cp2 = p2;
char c1 = *cp1,
c2 = *cp2;
if (c1 == c2)
return 0;
else
return c1 > c2 ? 1 : -1;
}
/* Sort cmd line args in two ways with qsort */
int main(int argc, char **argv) {
/* sort chars of each string */
for (int i = 1; i < argc; i++)
qsort(argv[i], // base / first
strlen(argv[i]), 1, // n_elems, elem_size
cmpchar);
/* sort argv strings 1 to argc-1 */
qsort(argv + 1,
argc - 1, sizeof *argv,
cmpstringp);
for (int j = 1; j < argc; j++)
puts(argv[j]);
return 0;
}
The second cmp function cmpchar has one extra assignment level. Without there would be just some harmless stars like if (*cp1 == *cp2).
How do I sort the elements of argv in C? has a solution like man page, with a cast directly in strcmp().
But isn't my cast-free approach more correct? I got warnings until I put the const where it belongs.
The const in the middle of char *const *sp1 = vep1 means that whatever is designated by *sp1 is const. This is required because the const void * says that the memory directly pointed to by the pointer is supposed to be const.
You're right in that const void * is not assignable to const char ** without a cast because it would not be const correct. I personally try to avoid casts as much as is meaningfully possible, and therefore would prefer your code over the one that uses casts.
However It would be somewhat more correct to use const char *const *sp1, because there is no reason why cmpstringp shouldn't actually work for const char ** too!
Reordered and aligned it becomes more clear what is converted and what is conserved from right to left:
int cmpstringp(void const *vep1, const void *vep2) {
char * const *sp1 = vep1, ...
The const is copied, and the "to void" is replaced by "to pointer (to char). It does not even have to be a pointer (e.g. a struct). If, then I doubt this const-ness can be spread onto all references, just because a "compare" function is not supposed to modify at all. It may modify all objects, but not the ones being sorted.
This is all very interesting but logically complicated. Like having a row of boxes with letters inside. First you sort the letters inside each box according to themselves (not touching the boxes), then you sort the boxes according to the letters (but not touching them, the letters).
Thank you for comments and answer.
6.5.16.1 Simple assignment probably handles this case.
3 bullets for pointer assignment seem to demand:
compatible types, or one is void
left side has all the qualifiers of the right side.
null pointer on the right is no problem
I've read through several similar questions on Stack Overflow, but I've not been able to find one that helps me understand this warning in this case. I'm in my first week of trying to learn C though, so apologies if I've missed an obvious answer elsewhere on Stack Overflow through lack of understanding.
I get the following warning and note:
warning: passing argument 2 of ‘CheckIfIn’ makes pointer from integer without a cast [enabled by default]
if(CheckIfIn(letter, *Vowels) ){
^
note: expected ‘char *’ but argument is of type ‘char’
int CheckIfIn(char ch, char *checkstring) {
When trying to compile this code:
#include <stdio.h>
#include <string.h>
#define CharSize 1 // in case running on other systems
int CheckIfIn(char ch, char *checkstring) {
int string_len = sizeof(*checkstring) / CharSize;
int a = 0;
for(a = 0; a < string_len && checkstring[a] != '\0'; a++ ){
if (ch == checkstring[a]) {
return 1;
}
}
return 0;
}
// test function
int main(int argc, char *argv[]){
char letter = 'a';
char *Vowels = "aeiou";
if(CheckIfIn(letter, *Vowels) ){
printf("this is a vowel.\n");
}
return 0;
}
Vowels is a char*, *Vowels is just a char, 'a'. chars get automatically promoted to integers, which your compiler is allowing to be implicitly converted to a pointer. However the pointer value will not be Vowels, it will be the address equal to the integer encoding of the character 'a', 0x61 almost universally.
Just pass Vowels to your function.
In your case, the type conversion is from char to integer pointer. In some cases, the function takes void pointer as the second argument to accommodate for all the data-types.
In such cases, you would need to typecast the second argument as (void *)
This would be the function declaration in most well written modular functions:
int CheckIfIn(char ch, void *checkstring);
You would need to pass the argument as a void pointer, provided the Vowels is not a char pointer
if(CheckIfIn(letter, (void *)Vowels) ){
printf("this is a vowel.\n");
}
I want to find size or length of an unsigned char pointer in a function where that pointer is an argument to that function.Near the pointers declaration size is coming correctly.
But when i am trying to find size in function it is giving 4.
How can i do this ?
#include <stdio.h>
//void writeFile(unsigned char *da);
void writeFile(char *da);
int main(int arc,char **argv)
{
unsigned char block_bmp[]=
{
0x0,0xff,0xff,0xff,0x0,0x0,0x0,0xff,0xff,0xff,0x0,0x0,0x0,0xff,0xff,0xff,//*16bytes*
};
printf("size::%d\n",sizeof(block_bmp));
writeFile(block_bmp);
}
//void writeFile(unsigned char *da)
void writeFile(char *da)
{
printf("%d\n",__LINE__);
printf("size::%d\n",sizeof(da));
printf("length::%d\n",strlen(da));
FILE *fp;
int i;
fp=fopen("/tmp/hexfile","wb");
for(i=0;i<3780;i++)
fprintf(fp,"%c",da[i]);
// fwrite(da,sizeof(unsigned char),sizeof(da),fp);
fclose(fp);
}
If it points to a NULL-terminated string, use strlen. If not, the pointer is just some memory address for the called function, without any additional information about the size of the array (I assume, you try to pass an array). I suggest passing the number of array elements as additional parameter to the function.
You can not use sizeof in this condition. You should be using the strlen if the char array is NULL terminated.
When you pass array to a function, it decays to pointer, you can't use sizeof on this pointer to get the size of the array. The sizeof will give you 4 bytes (assuming 32 bit machine).
Example:
#include <stdio.h>
void fun(int myArray[10])
{
int i = sizeof(myArray);
printf("Size of myArray = %d\n", i);
}
int main(void)
{
// Initialize all elements of myArray to 0
int myArray[10] = {0};
fun(myArray);
getch();
return 0;
}
/**************OUTPUT**************
Size of myArray = 4
***********************************/
unsigned char array[100];
// sizeof(array) == 100
unsigned char* ptr = array;
// sizeof(ptr) == 4 for 32 bit platform.
When you call a function such as
foo(unsigned char* ptr)
{
}
with 'array' as argument, you only see a 'unsigned char *', not an array with 100 elements. That's why 'sizeof' returns 4.
pointers size sizeof(any_pointer_variable) will not depend on the datatype to which it is going to point. The size of the pointer is mostly 4 that is what you are getting in function. and it doesnt depend on what it points to. all pointers will have same size independent of what type they point to.
I had a bit of a confusion. Below is a very simple example which works:
#include <stdlib.h>
typedef struct
{
unsigned char one: 1;
unsigned char two:1;
unsigned char three: 1;
unsigned char four: 1;
} nibble_bits;
typedef union
{
unsigned char all : 4;
nibble_bits bits;
} nibble;
void initArr(nibble ** arrLoc, unsigned int size)
{
nibble * start = arrLoc[0];
int i =0;
for (i=0; i<size; i++)
{
start[i].all = 0;
}
}
int main()
{
nibble * fourNibbles = (nibble *) malloc(4 * sizeof(nibble));
initArr(&fourNibbles,4);
}
This compiles fine with no warnings. However, when I change the first line in main:
nibble * fourNibbles = (nibble *) malloc(4 * sizeof(nibble));
to:
nibble fourNibbles[4];
I get the following:
warning: main.c: In function ‘main’:
main.c:150: warning: passing argument 1 of ‘initArr’ from incompatible pointer type
Upon running, I get a "Bus error 10".
Seems to me like the lines are doing the same thing, except that the malloc is allocating space for the array on the heap and the array declaration is on the stack. But (I thought) either way "fourNibbles" is of type "pointer to nibble", and hence the address of "fourNibbles" would be pointer to pointer to nibble (nibble **).
What am I missing here?
These are not even remotely the same. This
nibble * fourNibbles = (nibble *) malloc(4 * sizeof(nibble));
declares a pointer fourNibbles, while this
nibble fourNibbles[4];
declares an array. Arrays and pointers are two completely different things, which (at object level) have nothing in common. Trying to use them interchangeably in object contexts (like & operator) will only lead to disaster. There lots of information on this topic here on SO (search for "array pointer difference") as well as in this [de-facto standard] C FAQ: http://c-faq.com/aryptr/index.html
There is another thing that draws attention in your code though. Your function
void initArr(nibble ** arrLoc, unsigned int size)
is specifically tailored to the first variant, since it requires a pointer to a pointer as its first argument. It will not work if you attempt to force a pointer to an array to the first argument (which you already had a chance to observe firsthand).
However, the real question here is why your initArr function is written in such a bizarre way. This sequence
void initArr(nibble ** arrLoc, unsigned int size)
{
...
nibble * start = arrLoc[0];
...
start[i].all = 0;
looks rather unusual. Why are you passing a pointer to a pointer instead of an ordinary single-level pointer? E.g. you could simply do
void initArr(nibble *start, unsigned size)
{
unsigned i;
for (i = 0; i < size; ++i)
start[i].all = 0;
}
This version would be called as
initArr(fourNibbles,4); /* note: no `&` operator */
and it would be compatible with both malloc-ed arrays and explicitly declared arrays.
P.S. In C language a better idiom for malloc is
nibble * fourNibbles = malloc(4 * sizeof *fourNibbles);
Note that in this variant type name nibble is mentioned only once.
You are missing that the address of an array has a different type from the pointer that the plain array name becomes when used in an expression.
That is:
int *a1 = ...;
int a2[] = { ... };
some_func(&a1);
some_func(&a2);
cannot be correct unless some_func() expects a void *. The first call passes an int ** — a pointer to pointer to int; the second call passes an int (*)[] — a pointer to array of int. Drop the & from the array.
However, in your code, the problems are more complex. Because the function expects a nibble **, you have problems. What you should be doing is passing a nibble *:
void initArr(nibble *arrLoc, unsigned int size)
{
for (unsigned int i = 0; i < size; i++)
start[i].all = 0;
}
int main(void)
{
nibble *fourNibbles_1 = (nibble *) malloc(4 * sizeof(nibble));
nibble fourNibbles_2[4];
initArr(fourNibbles_1, 4);
initArr(fourNubbles_2, 4);
initArr(&fourNubbles_2[0], 4);
}
Your actual code is doing some really rather weird stuff. How much damage it is doing may depend on how big a pointer is compared to a nibble.
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.