I am new to C language and I have some misunderstanding in the following exercise:
void printAllStrings(const char** arr[])
{
while(*arr!=NULL)
{
char** ptr=(char**)*arr;
while(*ptr!=NULL)
{
printf("%s\n",*ptr);
ptr++;
}
arr++;
}
}
int main()
{
char* arrP1[]={"father","mother",NULL};
char* arrP2[]={"sister","brother","grandfather",NULL};
char* arrP3[]={"grandmother",NULL};
char* arrP4[]={"uncle","aunt",NULL};
char** arrPP[]={arrP1,arrP2,arrP3,arrP4,NULL};
printf("Before sort :\n");
printAllStrings(arrPP);
sort(arrPP);
printf("\nAfter sort :\n");
printAllStrings(arrPP);
printf("\nMaximum length string : %s \n",maxLengthString(arrPP));
return 0;
}
The code above prints all strings.
My questions is:
In printAllStrings function the the passed parameter(char** arr[]) array of strings, could we pass pointer on pointer - char** arr.
What the meaning of this row char** ptr=(char**)*arr; I undersatnd that this casting of pointers to char type.
But why the pointer have to be casted is already points to char type?
In printAllStrings function the the passed parameter(char** arr[]) array of strings, could we pass pointer on pointer - char** arr[].
In your examples above, you have char** arr[] and char** arr[] (the two are the same) so your "could we pass?" question is unclear. If you are asking if you could change the parameter to (char ***arr), then yes, you could because the first level of indirection (e.g. [ ]) is converted to a pointer.
What the meaning of this row char** ptr=(char**)*arr; I undersatnd that this casting of pointers to char type. But why the pointer have
to be casted is already points to char type?
The reason is your parameter is const char** arr[] and then you declare char** ptr which discards the const qualifier on arr. const char ** and char ** are not the same. So when you attempt to initialize ptr with the dereferenced arr, e.g. (char** ptr=arr;) the compiler complains about the discard of the const qualifier.
Rather than fixing the problem correctly, e.g.
const char **ptr = *arr;
you "fudge" the initialization to force the discard of the const qualifier -- resulting in ptr not retaining the const type which can prevent the compiler from warning when you attempt to use ptr in a non-constant way (really bad things happen when you just cast away const qualifiers)
I may be wrong -- but it looks like the point of the assignment is to have you preserve the const nature of the string literals you use to initialize your array of pointers. So rather than declaring the arrays as:
char* arrP1[]={"father","mother",NULL};
You should declare them as arrays of const pointers to char, e.g.
const char *arrP1[]={"father","mother",NULL};
Your parameter for printAllStrings then makes sense, and the compiler with warn if you try and do something you are not allowed to do like changing the string literals, e.g. if you try:
arrP1[0][0] = 'l';
(the compiler will throw error: assignment of read-only location ‘*arrP1[0])
If you carry the types consistently through your code, you will not have to "fudge" with any casts anywhere, and the compiler can help protect you from yourself. For example, a simple rework of your types to make sure your string literals are const qualified (while still allowing you to sort your arrays) could be done with something like:
#include <stdio.h>
#include <string.h>
void printAllStrings (const char **arr[])
{
while (*arr != NULL) {
const char **ptr = *arr;
while (*ptr != NULL) {
printf ("%s\n", *ptr);
ptr++;
}
arr++;
}
}
const char *maxLengthString (const char **arr[])
{
size_t max = 0;
const char *longest = NULL;
while (*arr != NULL) {
const char **ptr = *arr;
while (*ptr != NULL) {
size_t len = strlen (*ptr);
if (len > max) {
max = len;
longest = *ptr;
}
ptr++;
}
arr++;
}
return longest;
}
int main (void) {
const char *arrP1[] = {"father", "mother", NULL};
const char *arrP2[] = {"sister", "brother", "grandfather", NULL};
const char *arrP3[] = {"grandmother", NULL};
const char *arrP4[] = {"uncle", "aunt", NULL};
const char **arrPP[] = {arrP1, arrP2, arrP3, arrP4, NULL};
printf ("Before sort :\n");
printAllStrings (arrPP);
// sort (arrPP); /* you didn't post sort, so the following swaps */
const char **tmp = arrPP[0]; /* simple swap example */
arrPP[0] = arrPP[1];
arrPP[1] = tmp;
printf ("\nAfter sort :\n");
printAllStrings (arrPP);
printf ("\nMaximum length string : %s \n", maxLengthString (arrPP));
return 0;
}
(You didn't post sort(), so above the elements a simply swapped to show your arrPP retains the ability to be sorted, and a quick implementation of the maxLengthString () was added to make your last statement work -- but note, it just finds the first of any longest strings if more than one are the same length)
Example Use/Output
$ ./bin/array_ptp_const_char
Before sort :
father
mother
sister
brother
grandfather
grandmother
uncle
aunt
After sort :
sister
brother
grandfather
father
mother
grandmother
uncle
aunt
Maximum length string : grandfather
Look things over and let me know if you have further questions. I'm not sure if this is what you were looking for, but based on your code and questions, it seemed the most logical choice.
I can only answer the 2nd question as I can not understand the 1st the way it is now.
char** ptr=(char**)*arr;
// this is the same but maybe confusing because arr is a pointer now and it gets iterated in the loop.
char** ptr=(char**)arr[0];
arrhas the type pointer (decayed from an array) to pointer to pointer to char.
ptrhas the type pointer to pointer to char.
As you can see ptr has one level of reference less than arr. arrholds all the pointers to your declared arrays of type pointer to char. (arrP1, arrP2, arrP3, arrP4).
By dereferencing arr you get the pointer to one of these arrays.(arrP1 in the first iteration)
Then you print where the pointer stored in ptr[0] points to and iterate to ptr[1], to print this. After the loop arrgets iterated and yields the pointer to arrP2 and you start again with the ptr-loop.
You have to keep in mind that arrays and pointers are quite similar(but not exactly the same) in their usage and passing an array to function lets it decay to a pointer.
Edit: David's answer is great, I misread the focus of the 2nd question, so this answer is a bit offtopic. I will leave it here, because I think it still helps understanding what happens with all the pointer magic. For a relevant answer especially about const correctness David's answer is the one.
Related
I need to intialize an empty array of strings with fixed size ( 3 by 100 for example), pass it to a function to fill it with data and perform things like strcpy(), strcmp(), memset() on it. After the function is terminated I need to be able to read the data from my main().
What I tried so far:
char arrayofstrings[3][100] = {0};
char (*pointer)[3][100] = &arrayofstrings;
function(pointer);
Initalizing an (empty?) array of strings and initializing a pointer on the first element.
int function (char (*pointer)[3][100])
{
strcpy((*pointer)[i], somepointertostring);
strcmp((*pointer)[i], somepointertostring)
memset((*pointer)[i], 0, strlen((*pointer)[i]));
}
Is this a good way to do it? Is there an easier way to do it? Whats up with the brackets around the pointer?
C string functions expect a buffer to be null-terminated. Your arrayofstrings allocation happens on the stack. Depending on your compiler it might be initialized to all zeros or might contain garbage.
The simplest way in your case to make sure string functions won't overrun your buffers is to set the first character of each to 0 (null)
arrayofstrings[0][0] = 0x00;
arrayofstrings[1][0] = 0x00;
arrayofstrings[2][0] = 0x00;
This will give you 3, 100-char buffers that contain a valid empty "string". Note that you can only store 99 "characters" because the last character must be 0x00 (null-terminator).
char (*pointer)[3][100] = &arrayofstrings;
This is unnecessary.
Something to keep in mind about arrays in C is that the [] index is really only there to make things easier for the human programmer. Any array definition is simply a pointer to memory. The values inside the [][]...[] indexes and the type are used by the compiler to allocate the right amount of memory on the stack and do some simple math to come up with the right memory address for the element you want to access.
char arrayofstrings[3][100];
This will allocate sizeof(char)*3*100 bytes on the stack and give you a char* called 'arrayofstrings'. There's nothing special about the char* itself. It would be the same pointer if you had char arrayofstrings[300] or char arrayofstrings[3][10][10] or even long arrayofstrings[75] (char is 1 byte, long is 4 bytes).
Because you declared it as a multidimensional array with [a][b], when you ask for arrayofstrings[x][y], the compiler will calculate ((x*b)+y)*sizeof(type) and add it to the arrayofstrings pointer to get the address of the value you want. But because it's just a pointer, you can treat it like any other pointer and pass it around or cast it to other types of pointer or do pointer math with it.
You don't need the extra level of indirection.
An array, when passed to a function, is converted to a pointer to its first member. So if you declare the function like this:
int function(char (*pointer)[100])
Or equivalently:
int function(char pointer[][100])
Or:
int function(char pointer[3][100])
You can pass the array directly to the function:
function(arrayofstrings);
Then the body could look something like this:
strcpy(pointer[0], "some string");
strcpy(pointer[1], "some other string");
strcpy(pointer[2], "yet another string");
Best way to initialize an array of strings ...
char arrayofstrings[3][100] = {0}; is fine to initialize an array of strings.
In C, initialization is done only at object definition, like above.
Later code like strcpy(), assigns data to the array.
Best way to ... pass it to a function
When the C compiler supports variable length arrays, use function(size_t n, size_t sz, char a[n][sz]).
Add error checks.
Use size_t for array sizing and indexing.
#define somepointertostring "Hello World"
int function(size_t n, size_t sz, char arrayofstrings[n][sz]) {
if (sz <= strlen(somepointertostring)) {
return 1;
}
for (size_t i = 0; i < n; i++) {
strcpy(arrayofstrings[i], somepointertostring);
if (strcmp(arrayofstrings[i], somepointertostring)) {
return 1;
}
// Drop this it see something interesting in `foo()`
memset(arrayofstrings[i], 0, strlen(arrayofstrings[i]));
}
return 0;
}
void foo(void) {
char arrayofstrings[3][100] = {0};
size_t n = sizeof arrayofstrings / sizeof arrayofstrings[0];
size_t sz = sizeof arrayofstrings[0];
if (function(n, sz, arrayofstrings)) {
puts("Fail");
} else {
puts("Success");
puts(arrayofstrings[0]);
}
}
Initalizing an (empty?) array of strings and initializing a pointer on the first element.
The type of &arrayofstrings is char (*)[3][100] i.e. pointer to an object which is a 2D array of char type with dimension 3 x 100. So, this initialisation
char (*pointer)[3][100] = &arrayofstrings;
is not initialisation of pointer with first element of arrayofstrings array but pointer will point to whole 2D array arrayofstrings. That why, when accessing the elements using pointer you need bracket around it -
`(*pointer)[0]` -> first string
`(*pointer)[1]` -> second string and so on..
Is this a good way to do it? Is there an easier way to do it?
If you want pointer to first element of array arrayofstrings then you can do
char (*p)[100] = &arrayofstrings[0];
Or
char (*p)[100] = arrayofstrings;
both &arrayofstrings[0] and arrayofstrings are equivalent1).
Pass it to a function and access the array:
function() function signature should be -
int function (char (*pointer)[100])
// if you want the function should be aware of number of rows, add a parameter for it -
// int function (char (*pointer)[100], int rows)
this is equivalent to
int function (char pointer[][100])
and call it in from main() function like this -
function (p);
In the function() function you can access array as p[0], p[1] ...:
Sample program for demonstration:
#include <stdio.h>
#include <string.h>
#define ROW 3
#define COL 100
void function (char (*p)[COL]) {
strcpy (p[0], "string one");
strcpy (p[1], "string two");
strcpy (p[2], "string three");
}
int main(void) {
char arrayofstrings[ROW][COL] = {0};
char (*pointer)[COL] = &arrayofstrings[0];
function (pointer);
for (size_t i = 0; i < ROW; ++i) {
printf ("%s\n", arrayofstrings[i]);
}
return 0;
}
When you access an array, it is converted to a pointer to first element (there are few exceptions to this rule).
Currently, I'm studying c programming language and on the test was a task - Write a function that has two strings as a parameter and need to find and print all unique characters that are present in both strings:
the 'main' function for the test is (and I suspect that all tests are generated using similar function):
int main(void)
{
char *str0 = {"example string", "excelent string"};
solution(2, str0);
return (0);
}
Function should be declared as follow void solution(int size, char **strs);
I don't see any problem to write algorithm. The question is - how to get a second string from **strs?
I tried different approaches just to print second element:
printf("%s", strs[1]);
*strs++;
printf("%s", *strs);
But nothing works.
I guess, that mistake in main function:
Should be something like this: char *str0[] = {"example string", "excelent string"};
Could somebody help - is this mistake in the test and with a provided function to solve this task is impossible or is exist any way to get second string from array?
char *str0 = {"example string", "excelent string"}; is incorrect: it declares a character array, but attempts to define it using array of size 2 containing elements of the type 'character array'. It should be declared as an array of arrays, for example using a fixed size of 2. This works:
#include <stdio.h>
void solution(int size, char **strs)
{
printf("%s\n", strs[0]);
printf("%s\n", strs[1]);
}
int main(void)
{
char *str0[2] = {"example string", "excelent string"};
solution(2, str0);
return 0;
}
I do not consider it a good practice to mix the * and [] pointer notations, but this follows the specifications of the task best, given the definition of the string literal array of size 2.
Continuing from the comment, your brace-initializer {"example string", "excelent string"} is the initializer for an array. (in this case) Specifically in your case, it is the initializer for an array of pointers to String-Literals.
You declare your array and determine the number of elements by:
char *strings[] = { "one", "two" };
size_t nelem = sizeof strings / sizeof *strings;
Once you have your array of pointers, you can pass the array as type char ** because the first level of indirection is converted to a pointer on access. See C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
Your function declaration can then be:
void solution (size_t size, char **strs);
and you would invoke your function like:
solution (nelem, strings);
A short example would be:
#include <stdio.h>
void solution (size_t size, char **strs)
{
for (size_t i = 0; i < size; i++)
puts (strs[i]);
}
int main (void) {
char *strings[] = { "one", "two" };
size_t nelem = sizeof strings / sizeof *strings;
solution (nelem, strings);
return 0;
}
(note: while nelem and size can be type int, it is more properly size_t -- though the compiler will allow the result of the division to be assigned to int without warning)
Example Use/Output
Which will iterate over the strings outputting each from the function:
$ ./bin/array_ptrs_init
one
two
Determining Unique Characters
Now for the rest of your task outputting unique characters from both strings, you can include string.h and iterate over the characters in one string calling strchr() to determine if the character is present in the second string. If it is, continue; and get the next char to check, or it it is unique (e.g. strchr() returns NULL), output the character as unique. (this part I leave to you)
char* is the array of chars, that is, char * is string.
And equally, for some type of T, T* is considered as the array of T.
So, you assume that char * is a new type such as
typedef char * STRING;
Then you can understand easily.
#include <stdio.h>
typedef const char * STRING;
void solution(int size, STRING * strings)
{
int i = 0;
for (i = 0; i < size; i++)
{
printf("%s\n", strings[i]);
}
}
int main()
{
STRING strings[2] = { "example string", "excelent string" };
solution(2, strings);
return 0;
}
I wish to split a "string" by the character ','.
The string holds a GPS NMEA encoded string, but that is of no matter.
My problem is that sometimes the parameter from the function that processes this char array is empty... Like nothing is in the array.
How should I correctly pass a "char string[]" to a function so that I may operate on a that parameter as I sent it(as a char array, not a char pointer to an array).
I also need to specify that I'm using mikroC for PIC.
Here is my code as of right now:
char* GPS_sateliti;
char CsatInView[] =
"$GPGSV,3,2,11,14,25,170,00,16,57,208,39,18,67,296,40,19,40,246,00*74";
GPS_sateliti = GrupeazaDupaVirgule(CsatInView, 2);
char* GrupeazaDupaVirgule( char deGasit[],int nrVirgule ){
int cVirgule = 1;
char* pch = strtok (deGasit,",");
while (pch != 0)
{
pch = strtok (0, ",");
cVirgule++;
if(nrVirgule == cVirgule){
break;
}
}
return pch;
}
The function that operates on the char array received as a parameter in debug mode, before entering the function the char array is fine, after entering it, it seems to be empty
It may be that I should receive a pointer to an array of chars??
Any sort of advice is welcome.
Thank you
How should I correctly pass a "char string[]" to a function so that I may operate on a that parameter as I sent it(as a char array, not a char pointer to an array).
You can't. A function parameter of an array type always decays as the corresponding pointer type.
There are two idiomatic solutions to this.
1. a sentinel:
The last value in the array is a special value that marks the end. This is done in C with strings. They always end with a \0 character, that is guaranteed not to occur inside the string. The function can search for that character to know where the data ends.
(Note: with this info I have to add I'm not sure what your problem is. If you pass an "empty string", as literally "", the \0 will be there, so you shouldn't have a problem)
2. explicitly passing the size:
instead of just
void foo(int bar[]);
you define a function
void foo(size_t barSize, int bar[]);
The caller knows the size of the array, so it can just pass it along.
With a pointer :
char* arr;
yourFunction(arr);
If you wish to initialize it before :
char* arr = malloc(51 * sizeof(char)); // Allocate a memory place of 50 because strings are null terminated in C
yourFunction(arr);
An other way to allocate memory to an array :
char* arr = calloc(50, sizeof(char)); // Allocate 50 memory place which size if the size of a char
With a string :
char arr[50];
char* ptr = arr;
yourFunction(ptr);
You have to know that it is impossible in C to know the size of an array when using pointer. The only thing you can do is to parse the size of the string as a parameter :
size_t size = 50;
char arr[size];
char* ptr = arr;
yourFunction(ptr, size);
If you wish to understand in detail how pointer works and how to iterate them, may be this post can help you. I think it is very interesting.
Globally, you iterate through an array via a pointer like this :
for ( int i = 0; i < size; i++)
printf("Current pointed value in the array : %c\n", ptr[i]); // or arr[i]
I guess you understand why giving the size of a pointed array as a parameter is important. Sometimes you can avoid using this parameter like this :
for ( int i = 0; i != '\0'; i++) // Because strings are null-terminated in C.
// Do something
I am writing a simple function in C that should build a char array from string "abc" – so it should build {'a','b','c'} – and return a pointer to that array. Here is my code for this function:
char * makeArr()
{
static char arr[3];
sprintf(arr, "%s\n", "abc");
return arr;
}
Problems occur when I call this method in main:
int main(int argc, char *argv[])
{
char *arr[3];
arr = makeArr();
return 0;
}
The compiler is complaining about casting / conflicting types. I've been playing with pointers, casting and dereferencing for quite a while now, but can't seem to get it to work. Please let me know where my logic is wrong.
Hmm ... there are several errors in this code. Let's start with the most obvious your compiler complains about:
char *arr[3];
This line declares arr to be an array of three pointers to char. What you return from your function is a single pointer to a char -> doesn't match.
Next:
static char arr[3];
sprintf(arr, "%s\n", "abc")
Here you reserve 3 chars. the sprintf() will write 5 chars. %s is replaced by the 3 characters in your string literal "abc". You add a newline character and then a 0 is added as the marker for the end of the "string". Makes 5. This btw is undefined behavior. You write past the end of your array. Code like this can be compiled, but there's no guarantee at all about what will happen at runtime.
Doing a cut here. You should read about arrays and pointers in C. If the text you're reading claims they are the same ... stop right there and find a better text. They aren't.
I'll try to explain this here briefly, so it's suitable for the Q&A style.
An array in C indeed is a contiguous space of several values. char arr[3] means a variable that holds 3 chars.
On the other hand, a char * is just a pointer pointing to a char -- this could be the first element of an array.
In C, you can't pass arrays as function parameters, and you can't return arrays from a function. Trying to do so leads to an implicit conversion: What is actually passed is a pointer to the first element of that array.
I think the last bit of information missing is what a string literal in C is: it's an array (anonymous, e.g., it doesn't have a name) containing all the characters in the double quotes plus a 0 appended. The 0 marks the end of a "string" in C.
In an expression, a string literal evaluates to a pointer to the first element.
So, something like this:
char *foo = "bar";
will lead to foo pointing to the b of the array. It's like writing
static const char no_name_0[] = { 'b', 'a', 'r', 0 };
char *foo = &(no_name_0[0]);
Among other things, you confused:
char arr[3]; // array of 3 chars.
and,
char *arr[3]; // array of 3 pointers to char.
In main(), you should only write char *arr;
Firstly, char arr[3]; is too snall to store "abc\n". It must have at least 5 elements including terminating null-character.
Then, char *arr[3]; is a 3-element array of char*.
You should assign makeArr()'s return value (it has char* type) to arr[0] or another element, or you should change the type of arr in main function to char*, which is the same type as makeArr()'s return value.
Moreover, this makeArr() doesn't make any array and returns (a pointer to) the existing array. Yoy should use malloc() to "make an array".
UPDATE:
Assigning a value of char* to the array char arr[10]; seems invalid in C.
You should use strcpy() or strncpy() (safer than strcpy()) to copy the string stored in the array between arrays.
Pass the array as an argument and modify it in the called function, would be easier. If you're statically creating the array and there's no need to allocate memory, don't, just pass around your pointers to the functions to be modified by reference
void makeArr(char arr[]){
sprintf(arr, "%s\n", "abc");
}
Simply pass the existing declared array to the makeArr function...
int main(int argc, char *argv[]) {
char arr[10];
makeArr(arr);
return 0;
}
You couldn't assign the result of makeArr to arr. I guess that's your casting error. Oversimplifying, arr points to the place on the stack where the array of 10 characters is allocated. So, I'd pass in arr to makeArr as a char*. So, you'd end up with something like this:
#include <stdio.h>
char * makeArr(char *arr)
{
sprintf(arr, "%s\n", "abc");
return arr;
}
int main(int argc, char *argv[])
{
char arr[10];
makeArr(arr);
printf("%s\n", arr);
return 0;
}
I'm trying to have a list of cheeses that you can search through to get to a certain one. When I am done I also want to add info about each one. But every time I run my code I get the error shown in the title. Here is my code:
#include<stdio.h>
#include<string.h>
int main()
{
char cheeses[][20] = {
"Cheddar",
"White Cheddar",
"Colby Jack",
"Gouda",
"Blue Cheese",
"Gorgonzola",
"Asiago",
"Limburger",
"Feta",
"Brie",
};
void find_cheese(char search_for)
{
int i;
for (i = 0; i < 5; i++) {
if (strstr(cheeses[i], search_for))
printf("Cheese %i: '%s'\n", i, cheeses[i]);
}
}
int main()
{
char search_for[20];
printf("Search for: ");
fgets(search_for, 20, stdin);
find_cheese(search_for);
return 0;
}
}
Why is this happening? I've looked it up in my book, but can't seem to find the answer.
The second argument to strstr should be const char*. You're passing a single char.
strstr expects a pointer to char as the second argument:
const char * strstr ( const char * str1, const char * str2 );
while you are passing a char.
void find_cheese(char search_for)
should be
void find_cheese(char *search_for)
because it is looking for a string (char *), not a single character.
strstr() takes a string (char *) as the second argument.
For usage see here.
The problem in your code is you're accepting the passed the array incorrectly:
void find_cheese(char search_for) //This line is wrong
{
What you wanted to do was:
void find_cheese(char search_for[])
EDIT:
Here's what is happening. When you access the array in your main function:
find_cheese(search_for);
What you're actually doing here is passing an address. (When you just access the array search_for you get an address). So when your prototype said it was taking a char the compiler saw the address (4 bytes, looking like an int) and truncated it to fit in a one byte char.
The way you prevent that is by passing the whole "string" (a string in C is a character array). So you can do this by passing search_for as an array, or as a pointer (address to a character array).
void find_cheese(char search_for[])
and
void find_cheese(char *search_for)
Will both work and are equivalent for your needs (passing to strstr())