I have one char strings pointers array, lets call it "str_array".
I also have 3 strings:
1."Hello"
2."World"
3."Today"
(Every string ends with \0)
And I have this function, that receives our str_array, the size of it, and another pointers arr that is not releveant for my question.
The problem that I encounter, is that function "scanString" receives NULL, or garbage values, instead of the strings inside str_array.
unsigned int RemoveFromStrArray(char*** str_array, unsigned int str_array_size, char** ptr_to_chars_array)
{
int k = 0;
while (k != str_array_size)
{
// This is the part where im trying to scan str_array strings.
scanString(*str_array[k]);
str_array++;
k++;
}
}
int scanString(char* string)
{
int c = 0;
int counter = 0;
while (string[c] != '\0')
{
if (string[c] == 1)
{
moveOneBack(string, c);
c--;
counter++;
}
c++;
}
return c;
}
I've been trying multiple alternative ways to scan str_array string arrays.
But all of my times I just had to deal with garbage values or NULL strings.
How do I reach str_array strings, that would be passed by reference to scanString?
Picture of what I'm talking about:
BIG THANKS IN ADVANCE!
unsigned int RemoveFromStrArray(char*** str_array
You've got at least one too many indirections there. From your drawing, str_array refers to an array, so it's effectively a pointer, and the values in the array are pointers to char. You don't seem to be changing the array itself, e.g. you're not making str_array refer to some other array of strings, so there's no need to pass the address of the array. So char ** is closer to the type you want.
str_array++;
I'm not sure why you're incrementing str_array and using k as an index into the array. Although it's safe to modify parameters locally like that, it's nice to avoid it so that you can refer to them while debugging. Consider this:
unsigned int RemoveFromStrArray(char** str_array,
unsigned int str_array_size)
{
int k = 0;
while (k != str_array_size)
{
char *string = str_array[k];
scanString(string);
k++;
}
}
That is, the values in str_array are of type char *, so str_array[k] has that type. Copying it into a temporary variable string makes it a little easier to see what's going on. (I removed the last parameter, ptr_to_chars_array, to keep things simple and because it's not used. You'll add it back if you have plans for it, I'm sure.)
A for loop would be a little more compact but otherwise equivalent:
unsigned int RemoveFromStrArray(char** str_array,
unsigned int str_array_size)
{
for (int k = 0; k < str_array_size; k++)
{
char *string = str_array[k];
scanString(string);
}
}
Related
I am fairly new to C and I just fell on pointers, I have been doing exercises on it and I looked all over this site and google but I just can't find what I'm looking for. (maybe from inexperience with terminology)
Context I have to use a specific prototype (that the grading program uses) to pass me an array of strings however since I'm limited to 25 lines per function I have tried to pass that same array of strings to another function to no avail, so this was my workaround:
#include <stdlib.h>
int ft_ultimate_range(int **range, int min, int max)
{
int *arr;
int i;
int j;
j = 0;
i = min;
if (min >= max)
{
*range = NULL;
return (0);
}
arr = malloc(sizeof(int) * ((long long int)max - (long long int)min));
*range = arr;
if(arr == NULL)
return (-1);
while (i < max)
{
*range[j] = i;
i++;
j++;
}
return (j);
}
However I'm getting a EXC_BAD_ACCESS (code=1) on vs code at *range[j] = i;/ seg. fault when execute on the terminal.
I don't understand why this is as I'm trying to assign values to the passed array if I take the star out I get (incompatible int to pointer conversion assigning to 'int *' from 'int').
What is going on?
Also can you also please point me to a thread explaining how to pass arrays of strings (or int's between functions)?
Edit: The question if it helps. (I just want to understand what's wrong)
Create a function ft_ultimate_range which allocates and assigns an array of ints.
This int array should contain all values between min and max. Min included - max excluded.
Here’s how it should be prototyped :
int ft_ultimate_range(int **range, int min, int max);
The size of range should be returned (or -1 on error).
*If the value of min is greater or equal to max’s value, range will point on NULL and it should return 0
As pointed out in the comments, *range[i] can be read as *(range[i]), following the rules of operator precedence. This means you are attempting to step into memory adjacent to the address provided by the caller.
Using (*range)[i], or simply using the arr variable you created earlier, will solve this.
while (i < max) {
arr[j] = i;
i++;
j++;
}
With that said, your code is slightly convoluted. While I don't fully agree with the idea of line limits for functions, if those are the constraints you are going to learn under you'll want to try and break problems down into their simplest components & steps needed.
The only additional variables we need are the computed length of the range, and the index we are inserting values into. There's no need to cast our computed length due to implicit conversions. min already provides our starting value.
size_t range(int **dest, int min, int max) {
if (min >= max)
return *dest = NULL, 0;
size_t length = max - min;
if (!(*dest = malloc(sizeof **dest * length)))
return -1;
for (size_t i = 0; i < length; i++)
(*dest)[i] = min++;
return length;
}
Note in this example I've used size_t as it
[...] can store the maximum size of a theoretically possible object of any type (including array).
and is the most appropriate type for indexing said array. You'll want to change every instance of size_t to an int to match your spec, however.
In C, a string refers to a sequence of nonzero bytes, followed by the NUL terminating byte ('\0').
Examples of strings:
const char *foo = "hello, world";
char bar[] = "Alice";
char baz[] = { 'B', 'o', 'b', '\0' };
There are no strings in your code. An example of passing an array of strings can be found by looking at the main function, and its second argument.
I'm working on a minisql code in C and i having some issues to allocate array of strings. I made a function called "alocaString" to do this (bc i'm using that a lot), but i don't think is working.
When the code reaches the line "strncpy(lista[qtnPalavras], splitStr, 100);" in the function "listaPalavras" (that have the purpose of split a string in different types of characters) a file named "strcpy-avx2.S" is created, one of the arguments of that function (**lista) is allocated with "alocaString" so i think the problem is in that function.
I already try to use valgrind and shows "is used uninitialized in this function [-Werror=uninitialized]" to all arrays of strings that i tried to use on that function, but i'm initializing them inside of the function
int alocaString (char **string, int tamanho, int posicoes){
string = malloc (posicoes * sizeof(char*));
for (int i = 0; i < posicoes; i++){
string [i] = malloc (tamanho * sizeof(char));
if (string[i] == NULL){return 0;}
}
return **string;
}
void desalocaString (char **string, int posicoes){
for (int i = 0; i < (posicoes); i++){
free (string[i]);
}
free (string);
}
int listaPalavras(char *entrada, char **lista, char *separador){ // lista as palavras
char *splitStr;
int qtnPalavras = 0;
splitStr = strtok(entrada, separador);
while (splitStr != NULL){
strncpy(lista[qtnPalavras], splitStr, 100);
qtnPalavras++;
splitStr = strtok(NULL, separador);
}
return qtnPalavras;
}
I assume that you are using these functions like this:
alocaString(lista, tamanho, posicoes);
listaPalavras(some_string, lista, some_delimiters);
desalocaString(arr);
Even without looking at the code, it seems logically wrong to allocate an array of strings first and then populate it if you do not already know how many strings it will need to fit. If you happen to allocate an array of n strings, but your listaPalavras() functions splits the provided string into n+1 or more substrings, you're going to overflow your previously allocated array. Nonetheless, this can be done taking the appropriate precautions, like carrying around sizes and checking them to avoid overflow.
The only sane way to achieve what you want is therefore to either (A) count the number of delimiters in the string first to know in advantage how many pointers you will need or (B) dynamically allocate the needed amount in listaPalavras() while splitting. You seem to be going with something similar to option A, but your code is flawed.
The desalocaString() is the only function that seems correct.
A correct implementation of alocaString() would return the allocated array (or NULL in case of failure), but you are returning **string which is just the first character of the first string. Needless to say, this does not make much sense. You don't need to take a char ** parameter, just the sizes. Secondly, in case of failure of any of the calls to malloc() you should free the previously allocated ones before returning NULL.
char **alocaString (unsigned tamanho, unsigned posicoes) {
char **lista = malloc(posicoes * sizeof(char*));
if (lista == NULL)
return NULL;
for (unsigned i = 0; i < posicoes; i++) {
lista[i] = malloc(tamanho * sizeof(char));
if (lista[i] == NULL) {
for (unsigned j = 0; j < i; j++)
free(lista[j]);
free(lista);
return NULL;
}
}
return lista;
}
As per listaPalavras(), which has the job of splitting the given string into other strings and copying them into the previously allocated array, to avoid overflowing the given array of strings you will need to also provide its length as well as the length of the previously allocated strings as argument (let's call them posicoes and tamanho like for the above function). Moreover, strncpy() will not add a NUL-terminator (\0) to the destination string if it is not found in the source string within the first n characters (n being the third argument), so you will need to add it yourself to make sure your strings are correctly terminated.
unsigned listaPalavras(const char *entrada, char *separador, char **lista, unsigned posicoes, unsigned tamanho) {
char *splitStr;
unsigned qtnPalavras = 0;
splitStr = strtok(entrada, separador);
while (qtnPalavras < posicoes && splitStr != NULL){
strncpy(lista[qtnPalavras], splitStr, tamanho);
lista[qtnPalavras][tamanho - 1] = '\0';
qtnPalavras++;
splitStr = strtok(NULL, separador);
}
return qtnPalavras;
}
Finally the code of the caller should look something like this:
char **lista;
unsigned tamanho = 100;
unsigned posicoes = 10;
unsigned palavras;
lista = alocaString(tamanho, posicoes);
if (lista == NULL) {
// handle the error somehow
}
palavras = listaPalavras(YOUR_STRING, YOUR_DELIMITERS, lista, posicoes, tamanho);
desalocaString(lista);
This should work fine, however you are limited by the fact that:
You cannot know beforehand the number of substrings that strtok() will find.
You cannot know beforehand the length of any of those substrings.
Therefore, allocating the needed lista dynamically inside listaPalavras() would make more sense.
Finally, as a side note, the names of your functions are misleading: if you need to allocate an array of strings, you might want to choose a better name than alocaString() which seems to imply that you are allocating a single string. Maybe alocaLista() and dealocaLista() would be better choices.
I'm sure this question has been asked before, but I've been searching pretty constantly for the last 2 days and can't solve this, so I'm hoping someone will help me out.
For my C assignment, we are supposed to receive each function parameter as a pointer. One of the values passed is a 2D char array, and for the last 48+ hours, I haven't been able to find out how to properly do this in my book, notes, instructor slides, etc. and actually make it work.
Simplified code of what I'm trying to accomplish
void main(){
/*
This is the value being passed. The first dimension has a size of 1000,
to account for 1000 different sorted people. Second dimension is an
array that holds the actual name, with a max name length of 25.
*/
char names[1000][25] = {'\0'};
read_files(names);
}
void read_files(char* names){
char newName1[4] = "mary";
char newName2[4] = "anna";
for(int i = 0; i < 5; i++){
names[0][i] = newName1[i];
}
for(int i = 0; i < 5; i++){
names[1][i] = newName2[i];
}
}
Basically, I'm trying to get names[0][x] to have x = "mary", and names[1][x] to have x = "anna".
(Or, more literally, names[0][0]= 'm', names[0][1]= 'a', etc.)
Unfortunately I can't get the passing right. The closest I've gotten is to have it assign one name to names, but not anymore.
Any help with this would be fantastic. There's actually quite a few classmates I'm working with who are all stumped on this. I'm sure its easy for experienced guys, but we just haven't gotten good instruction on how to do it and I can't find many specific examples that address this exact issue.
As I said, our instructor specifically wants function arguments to be pointers.
EDIT
Good info so far, I was very close to a few of these examples. I'll give them a shot and see what works.
You would have to define read_files as this:
void read_files(char (*names)[25]) {
char newName1[5] = "mary";
char newName2[5] = "anna";
for(int i = 0; i < 5; i++){
names[0][i] = newName1[i];
}
for(int i = 0; i < 5; i++){
names[1][i] = newName2[i];
}
}
Because you need to pass a pointer to an array of char[25]. A two
dimensional array cannot be converted into a double-pointer or a single pointer.
Note also that newName1 and newName2 must be arrays that can hold 5 elements. In C a
string is a sequence of characters that ends with the '\0'-terminating byte.
That means that for a string of length n, you need a char array of length
n+1, because you need the extra byte for the '\0'-terminating byte.
Also the better way of copying strings is strcpy or strncpy. strncpy is
more safe than strcpy because you limit the amount of bytes to be copied, thus
avoiding a buffer overflow. But strncpy might not write the '\0'-terminating byte
if there is not enough room.
Instead of:
for(int i = 0; i < 5; i++){
names[0][i] = newName1[i];
}
use this:
strcpy(names[0], newName1);
Or the strncpy way:
strncpy(names[0], newName1, sizeof names[0]);
names[0][sizeof(names[0]) - 1] = 0; // making sure to add the \0 byte
Also bear in mind, that the correct definition of the main function can be
only one of these:
int main(void);
int main(int argc, char **argv);
int main(int argc, char *argv[]);
#include <stdio.h>
void read_files(char* names){
char newName1[4] = "mary";
char newName2[4] = "anna";
for(int i = 0; i < 5; i++){
names[i] = newName1[i];
}
int offset = 25;
for(int i = 0; i < 5; i++){
names[i + offset] = newName2[i];
}
}
void main(){
/*
This is the value being passed. The first dimension has a size of 1000,
to account for 1000 different sorted people. Second dimension is an
array that holds the actual name, with a max name length of 25.
*/
char names[1000][25] = {'\0'};
read_files(names[0]);
printf("%s %s", names[0], names[1]);
}
In memory a 2d array is still a 1d block with fancy addressing. So you could have an offset variable and just add it on to every name like I have done here
I'm trying to build a 2 dimensional array by using str.cpy, but the program fails. The code receives 4 arrays add copies their content to their matching arrays.
int InsertStudent(char *firstName, char* lastName, char* dynCourses, char *dynGrades,
char firstNames[50][20],
char familyNames[50][20], char courses[50][5][20],
char grades[50][5])
{
int set,
int cset = 0;
for (set = 0; set <= (50); set++)
{
if (firstNames[set][cset] == '\0')
{
strcpy(firstNames[set][cset], firstName);
strcpy(familyNames[set], lastName);
for (cset = 0; cset <= 5; cset++)
{
strcpy(courses[set], dynCourses);
strcpy(grades[set], dynGrades);
}
}
}
return 0;
}
Well clearly the error is using strcpy wrongly.
The correct way would be
strcpy(firstNames[set], firstName);
Also in the loop it should be
for (cset = 0; cset < MAX_COURSES; cset++)
{
strcpy(courses[cset], dynCourses);
strcpy(grades[cset], dynGrades);
}
Note that the idiomatic C loop is for (int i = 0; i < MAX; i++), using < and not <=.
The signature of the strcpy function is
char *strcpy(char * restrict s1, const char * restrict s2);
Earlier you passed in place of s1 a char instead of char*. You must have got some warning (if enabled). If not then turn all compiler flags -Wall -Werror.
if (firstNames[set][cset] == '\0')
But if you are initially checking an uninitilized value with \0. This will higly unlikely will turn out to be false. There is no gurantee that the char array which doesn't contain strings will be initialized with 0 automatically. So make sure you have initialized the char arrays in the callee function like this
char arr[20][50]={0};
The loop is from 0 to MAX_STUDENTS. You are invoking an undefined behavior looping on array index out of bound if MAX_STUDENTS is greater than or equal to 50. Same goes for MAX_COURSES. More clearly the looping would be for (set = 0; set < (50); set++).
Again there will be a lot better way to deal with it if you put the initialization and copy part seperate. Otherwise this will be uneasy to maintain.
Seeing your use courses it is obvious that you want to declare it like this
char courses[5][20];
same goes for the grades array. Here you were trying to copy a string into into a 2d array. Compiler would complain about type incompatibility.
Also in the function you didn't return anything meaningful. The correct way would be to return the index value on which new name or information is added.
Here you are copying content of dynGrades and dynCourses to the array. So they will all contain the same values. Is this what you want? Because then what's the use of keeping 5 seperate char arrays - one could serve the purpose pretty well.
Working in C, I'm filling an array with char* return values from a function
char* files[4][12];
int i = 0;
for (;;)
{
char* file = get_value();
strcpy(files[i],file);
i++;
if (i > 4 || external_condition)
break;
}
// When I break out of
// my for loop the following
// code executes
for (i = 0; i < 5; i++)
{
if (files[i] != NULL)
manipulate(files[i]);
}
My problem is that if I break out of the first for loop without assigning values to all elements of files, my comparison in the second for loop fails. If only files[0] and files[1] have content, the loop processes files[2],file[3] and files[4] anyway.
files is declared as an "array of arrays of pointers to char". Or if you prefer, as a two-dimensional array of pointers to char.
So files[i] is of type "array of pointers to char" but you use it as just a "pointer to char". That is wrong.
That said, it is not clear what you want to do... maybe just:
char files[5][13];
will make more sense. 13 because you likely need 13 char strings (8.3 are 8+3+1=12 plus 1 for the ending NUL), and you seem to use 5 of them. Then initialize them to zero:
memset(files, 0, sizeof(files));
And use the check:
if (files[i][0])
to check if a text is initialized.
char* files[4][12] is a 2D array of char *, not char. Perhaps you meant your code to be as follows... I suggest you listen to what others have said. I'm merely posting a shortened version that still works.
char files[5][12] = { { 0 } };
int i = 0;
do {
strcpy(files[i], get_value());
} while (++i <= 4 && !external_condition);
while (i) {
manipulate(files[--i]);
}