How can I copy individual chars from a char** into another char**? - c

I'm trying to write a program to break down words into segments/syllables if they are entered in a specific format. I want to enter a word in the format:
[d-i-s][c-o-#][v-er-#]
and store it in the format:
syllable[0]=dis
syllable[1]=co
syllable[2]=ver
So far I have managed to break it into syllables using the delimiter ']' and save it as a char** in the format below:
syllable[0]=[d-i-s
syllable[1]=[c-o-#
syllable[2]=[v-er-#
So now I just want to clean it up and strip out the unnecessary characters! I thought I would make a new array and copy over the letters from the old array so long as they aren't [ - #. But for the life of me I cannot work out how to copy the right letters into another array!!
I know that I cant just do:
cleanArray[i][j] = dirtyArray[i][k]
Because cleanArray[i] is a char* and I can't edit it right? but what can I do?
I've read a lot of similar questions which have suggested strncpy and snprintf (how to copy char array to another char array in C?, strcpy and printf a multidimensional char array C) but I've tried those and I can't make them work. I've even tried putting cleanArray into 3 dimensions in the hope that I would then be able to save the individual letters into cleanArray[i][j] as char*s, which is probably completely wrong.
What is the right way of going about this? Sorry if it's obvious but I've spent hours on it and am now very, very, confused.. I would really appreciate any advice you can give!
Here's my code:
char** cleanStrings (char**dirtyList, int arrayLength)
{
int i, j, k;
char** cleanList = (char**)calloc(arrayLength, CHARLEN);
for (i=0; i<arrayLength; i++)
{
k= 0;
cleanList[i] = (char*)calloc(10,CHARLEN);
for (j=0; j<strlen(dirtyList[i]+1);j++)
{
if (dirtyList[i][j] == '[') continue;
else if (dirtyList[i][j] == '#') continue;
else if (dirtyList[i][j] == '-') continue;
else
//i know this is wrong, but what is the right way of doing it?
cleanList[i][k] = dirtyList[i][j];
k++;
}
}
return cleanList;
}
EDIT
Thanks for all your comments, I've now got it working! Contrary to what I thought, as Barmar points out there is nothing wrong with:
cleanArray[i][j] = dirtyArray[i][k]
My code didn't work because I made a lot of other mistakes like:
-casting the return values of calloc
-not allocating the memory properly for calloc
-incorrect brackets
I also had the code in a header file which I think contained problems of its own.

Assuming that you are using calloc size parameter wrong. One of these char** cleanList = (char**)calloc(arrayLength, CHARLEN); or cleanList[i] = (char*)calloc(10,CHARLEN); is wrong. You also should not cast the return value of a malloc() / calloc(). For legibility and code flow purposes I also replaced ifs statements. You also wrote for (j=0; j<strlen(dirtyList[i]+1);j++) instead of for (j=0; j<strlen(dirtyList[i])+1;j++) because strlen() calculates the length of the string without \0.Here is the code with few changes.
char** cleanStrings (char**dirtyList, int arrayLength)
{
int i, j, k;
char **cleanList = calloc(arrayLength,sizeof * cleanList);
for (i=0; i<arrayLength; i++)
{
k= 0;
cleanList[i] = calloc(10,sizeof * cleanList[i]);
for (j=0; j<strlen(dirtyList[i])+1;j++)
{
if ((dirtyList[i][j] != '[') && (dirtyList[i][j] != '#') && (dirtyList[i][j] != '-') ){
cleanList[i][k] = dirtyList[i][j];
k++;
}
}
}
return cleanList;
}

You're not allocating enough memory for cleanList. I assume CHARLEN is sizeof(char), which is 1 byte. But the elements of cleanList are char*, which is either 4 or 8 bytes, the allocation is much too small. It should be:
char **cleanList = calloc(arrayLength, sizeof(char *));
The general rule when using malloc or calloc is that the multiplier is always sizeof (T), where T is the destination type with one less *. So if you're assigning to char **, it's sizeof(char *).

Related

Why is my for loop increasing the size of my array in C?

I'm trying to make a binary number calculator in c and I'm running into issues of my for loops doubling the size of my second array and adding the first array onto the end. I'm really confused because I thought you couldn't increase the size after already declaring it. It is happening in my equation reading function as well but in this ones complement function it's a bit simpler to see. Any ideas of how to fix this?the codethe output
welcome to stack-overflow. From next time please use inline code editor to put your code instead of images. I have taken effort put your code in the answer itself to explain the problem. Please consider this as courtesy. Its very unusual to do it. But as you are a new member, I'm doing it.
// Cole carson's original code from image:
char * onescomp(char x[16], char y[16]){
int i;
for(i=0;i<=15;i++){
if(x[i] == '0'){
y[i] = '1';
continue;
}
else if(x[i] == '1'){
y[i] = '0';
continue;
}
}
return y;
}
int main()
{
char b3n[16]={'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
char cb3n[16];
puts(b3n);
onescomp(b3n,cb3n);
puts(cb3n);
return 0;
}
Answer:
You don't need continue; in if-else blocks.
You need to add '\0' in the last cell of cb3n array. so puts() knows when string ends & stop printing.
so to quickly fix this issue you can create array with extra cell and assign all values as '\0'. so after copying fifteen 1's there will be '\0' in the end. I think in your case those extra zeros being printed might be garbage values. It looks like array is doubling but it isn't, its just printing values beyond allocated memory because '\0' has not been provided.
//Quick fix
int main()
{
char b3n[16]={'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
char cb3n[17]={'\0'}; // <--- quick fix
puts(b3n);
onescomp(b3n,cb3n);
puts(cb3n);
return 0;
}

How to use realloc in an if/else loop in C with structs?

I have a quiz game in C where I am using a struct to save when a user enters a wrong answer and the corresponding correct answer to that question. First I used malloc to allocated memory for a single struct.
Struct:
typedef struct
{
char* wrongAnswers;
char* corrections;
} Corrections;
Malloc:
Corrections* corrections = (Corrections*)malloc(sizeof(Corrections));
Later on in my program, I have functionality where a wrong answer increments an 'incorrectAnswers' variable, which is used to reallocate the memory to allow for the new wrong answer to be stored, along with its corresponding correct answer.
Code:
// Extract characters from file and store in character c
for (c = getc(fPointerOpen); c != EOF; c = getc(fPointerOpen)) {
if (c == '\n') // Increment count if this character is newline
numberOfLines++;
}
for (int i = 0; i < numberOfLines; i++) {
int lengthOfQuestion = 150;
if (v == 0) {
printf("Correct\n");
score++;
}
else {
printf("Incorrect\n");
incorrectAnswers++;
corrections = (Corrections*)realloc(corrections, incorrectAnswers * sizeof(Corrections));
corrections[i].wrongAnswers = malloc(sizeof(char) * lengthofanswer);
corrections[i].wrongAnswers = lines[i].userAnswers;
corrections[i].corrections = malloc(sizeof(char) * lengthofanswer);
corrections[i].corrections = lines[i].answers;
}
printf("Your score is %d/%d\n", score, (i + 1));
}
I am receiving a bug depending on the order in which right and wrong answers are input. I have tried using free() in different parts of the program and I have noticed that the bug will always appear when I enter a wrong answer as the last entry in my program/if I enter a right then wrong answer. Why is this the case? My understanding is I am implementing realloc incorrectly.
you should use strcpy to copy string, do not use = to assign string in c.
strcpy(corrections[i].wrongAnswers,lines[i].userAnswers);
strcpy(corrections[i].corrections,lines[i].answers);
Each time you use malloc or realloc, you should check the return value of these functions.

reading strings to a char array and then getting the size of the strings

Im working on a project and I am stumped on this part.
I need to read words from stdin and place them in a char array and use an array of pointers to point to each word since they will be jagged. where numwords is an int read in representing the number of words.
char words[10000];
char *wordp[2000];
the problem is that I can only use the pointers to add the words.I can no longer use the [] to help.
*wordp = words; //set the first pointer to the beginning of the char array.
while (t < numwords){
scanf("%s", *(wordp + t)) //this is the part I dont know
wordp = words + charcounter; //charcounter is the num of chars in the prev word
t++;
}
for(int i = 0;words+i != '\n';i++){
charcounter++;
}
any help would be great I am so confused when it comes to pointers and arrays.
Your code will be much more manageable if you use an additional pointer
reference and increment that directly. In this way you won't have to do any
mental math. Additionally you need to be incrementing the reference before
reading in the next string, scanf doesn't move the pointer for you.
char buffer[10000];
char* words[200];
int number_of_words = 200;
int current_words_index = 0;
// This is what we are going to use to write to the buffer
char* current_buffer_prt = buffer;
// quick memset (as I don't remember if c does this for us)
for (int i = 0; i < 10000; i++)
buffer[i] = '\0';
while (current_words_index < number_of_words) {
// Store a pointer to the current word before doing anything to it
words[current_word_index] = current_buffer_ptr;
// Read the word into the buffer
scanf("%s", current_buffer_ptr);
// NOTE: The above line could also be written
// scanf("%s", words[current_word_index]);
// this is how we move the buffer to it's next empty position.
while (current_buffer_ptr != '\n')
current_buffer_ptr++;
// this ensures we don't overwrite the previous \n char
current_buffer_ptr++;
current_words_index += 1;
}
What you want to do is relatively straightforward. You've got an array of 10,000 chars for storage, and 2000 pointers. So to start with you'll want to assign the first pointer to the start of the array:
wordp[0] = &words[0];
In pointer form this is:
*(wordp + 0) = words + 0;
I've used the zeros to show how it relates to the arrays. In general, to set each pointer to each element:
*(wordp + i) == wordp[i]
words + i == &words[i]
So all you need to do is keep track of where you are in the pointer array, and as long as you've assigned correctly, the pointer array will keep track of the position in your char array.

Use of array of arrays of string in C for parsing text file

I would like to read from N text files (having similar structure: a few lines, each line having the same small number of words) and store in a string matrix the words read, in such a way that in each (row, col) position I have one word.
A simple (two lines, three words per line) specimen for the files is the following:
line1word1 line1word2 line1word3
line2word1 line2word2 line2word3
Delimiter for the words is space.
I have attempted this code:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_LENGTH 1000
#define MAX_TOKS 100
#define DELIMITERS " "
// line parsing utility
int parseString(char* line, char*** argv) {
char* buffer;
int argc;
buffer = (char*) malloc(strlen(line) * sizeof(char));
strcpy(buffer,line);
(*argv) = (char**) malloc(MAX_TOKS * sizeof(char**));
argc = 0;
(*argv)[argc++] = strtok(buffer, DELIMITERS);
while ((((*argv)[argc] = strtok(NULL, DELIMITERS)) != NULL) &&
(argc < MAX_TOKS)) ++argc;
return argc;
}
int main() {
char S[MAX_STRING_LENGTH];
char **A;
int n,i,j,l;
FILE *f;
char file[50];
char ***matrix;
matrix = malloc(MAX_TOKS * sizeof(char**));
//memory allocation for matrix
for (i = 0; i < MAX_TOKS; i++)
{
matrix[i] = malloc(MAX_TOKS * sizeof(char *));
for (j = 0; j < MAX_TOKS; j++)
{
matrix[i][j] = malloc(MAX_TOKS * sizeof(char));
}
}
int NFILE = 10; // number of files to be read
for(i=0;i<NFILE;i++)
{
sprintf(file,"file%d.txt",i);
f = fopen(file,"r");
l=0; // line-in-file index
while(fgets(S,sizeof(S),f)!=NULL) {
n = parseString(S,&A);
for(j=0;j<n;j++) {
matrix[i][l]=A[j];
printf("%s\t%s\n",matrix[i][l],A[j]);
}
l++;
}
fclose(f);
}
free(matrix);
free(A);
return(0);
}
The problem I can't solve is that there when checking for correspondance between the arrays (in order to be sure I am storing the single words correctly) using
printf("%s\t%s\n",matrix[i][l],A[j]);
I find that the last word (and only the last one) of each line, regardless of the file number, is not stored in matrix. That is to say, line1word1 and line1words of file0 are correctly stored in matrix[0][0][0] and matrix[0][0][1], but in the field matrix[0][0][2] there isn't line1word3, even if A[2] has it!
What am I doing wront? Any suggestion?
Many thanks in advance,
cheers
char ***matrix doesn't declare a three dimensional array. Your matrix would need to be something like char *matrix[a][b] to hold a two dimensional array of string pointers. In order to calculate addresses within an array, the compiler needs to know the all of dimensions but one. If you think about it, you will probably see why...
If you have two arrays:
1 2 3 1 2 3 4 5 6 7
4 5 6 8 9 10 11 12 13 14
7 8 9 15 16 17 18 19 20 21
You can see that item[1][1] is NOT the same item. Regardless of the dimensions in your array, the elements are typically arranged sequentially in memory, with each row following the previous (or possible column, depending on language, I suppose.) If you have an array of pointers, the actual content may be elsewhere, but the points would be arranged like this. So, in my examples above, you must provide the compiler with the number of columns so that it can find members (the number of rows can be variable.) In a three dimensional array, you must provide the first TWO dimensions so that the compiler may calculate item offsets.
I hope that helps.
EDIT: You can have truly dynamic array dimensions by creating your own function to process all array item accesses. The function would need to know the dynamic dimensions and the item index(s) so that it could calculate the appropriate address.
This looks wrong: buffer = (char*) malloc(strlen(line) * sizeof(char));
Firstly, there is no need to cast malloc in C. If your code doesn't compile without the cast, there are two possible reasons:
There is no prototype for malloc. Obviously this can cause problems, because no prototype means the function returns a default type: int, or an error occurs. This can cause your program to misbehave. To avoid this, #include <stdlib.h>.
You're using a C++ compiler. Stop. Either program in C++ (stop using malloc) or use a C compiler. If you want to use this project in a C++ project, compile your C code with a C compiler and link to it in your C++ compiler.
Secondly, sizeof(char) is always 1. There is no need to multiply by it.
Thirdly, a string is a sequence of characters ending at the first '\0'. This means a string always occupies at least 1 character, even if it is an empty string. What does strlen("") return? What is sizeof("")? You need to add 1 to make room for the '\0': buffer = malloc(strlen(line) + 1);.
This looks slightly wrong: (*argv) = (char**) malloc(MAX_TOKS * sizeof(char**));
malloc returns a pointer to an object. *argv is a char **, which means it points to a char *. However, in this case malloc returns a pointer to char ** objects. The representation isn't required to be identical. To avoid portability issues assosciated with this, follow this pattern variable = malloc(n * sizeof *variable); ... in this case, *argv = malloc(MAX_TOKS * **argv);
It gets more gritty as it goes. Forget everything you think you know about your code; Pretend you're going to come back to this in 24 months. What are you going to think of this?
argc = 0;
(*argv)[argc++] = strtok(buffer, DELIMITERS);
while ((((*argv)[argc] = strtok(NULL, DELIMITERS)) != NULL) &&
(argc < MAX_TOKS)) ++argc;
There's actually an off-by-one here, too. Assuming argc == MAX_TOKS, your loop would attempt to assign to (*argv)[MAX_TOKS]. This loop is where I believe your problem lies, and the solution is to express your intent more clearly rather than attempting to cram as much code into one line as possible. How would you rewrite this? Here's what I'd do, in this situation:
char *arg;
size_t argc = 0;
do {
arg = strtok(buffer, DELIMITERS);
buffer = NULL;
(*argv)[argc] = arg;
argc++;
} while (argc < MAX_TOKS && arg != NULL);
The problem is that your parsing loop doesn't increment when strtok returns NULL. Hence, your function returns the position of the last item. Supposing you had two tokens, your parsing function would return 1. Your display loop displays items up to, but not including this position: for(j=0;j<n;j++). You could use the suggested improvement, or change your loop: for (j = 0; j <= n; j++). Either way, you'll need to fix those off-by-ones.
Out of curiosity, which book are you reading?

qsort not sorting and strange output

so I'm using C, I cant seem to get this to work right. It's an array of pointers to structs which contain some contact info. I can't seem to get the qsort to sort correctly.
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
#define ELEMENTS 50
int sortZips(const void *a, const void *b);
typedef struct contactInfo {
char name[MAX];
char street[MAX];
char cityState[MAX];
char zipCode[MAX];
} contacts;
int main() {
int i = 0;
contacts **contactArray = malloc(ELEMENTS * sizeof(contacts *));
/* allocate array */
for (i = 0; i < ELEMENTS; i++) {
contactArray[i] = malloc(sizeof(contacts));
}
/* populate array */
for (i = 0; i < ELEMENTS; i++) {
fgets(contactArray[i]->name,MAX,stdin);
fgets(contactArray[i]->street,MAX,stdin);
fgets(contactArray[i]->cityState,MAX,stdin);
fgets(contactArray[i]->zipCode,MAX,stdin);
printf("%s", contactArray[i]->name);
printf("%s", contactArray[i]->street);
printf("%s", contactArray[i]->cityState);
printf("%s", contactArray[i]->zipCode);
}
printf("\n");
/* qsort((void *)contactArray, ELEMENTS, sizeof(contacts *), sortZips); */
for (i = 0; i < ELEMENTS; i++) {
fputs(contactArray[i]->name,stdout);
fputs(contactArray[i]->street,stdout);
fputs(contactArray[i]->cityState,stdout);
fputs(contactArray[i]->zipCode,stdout);
}
}
/* sortZips() sort function for qsort */
int sortZips(const void *a, const void *b) {
const contacts *ia = *(contacts **)a;
const contacts *ib = *(contacts **)b;
return strcmp(ia->zipCode, ib->zipCode);
}
The output is printing the addresses (I have 50 in an input file) and then some random characters, like a huge block of them, then the sorted list after that which is messed up and not sorted right.
Please any help would be appreciated. I need to learn what's wrong here and why.
Thanx.
First rule: always check input functions - in this case, fgets(). You don't know whether everything is working correctly or not if you do not check.
Second: use enum in preference to #define in general.
With the check for early EOF in place, your code sorted my sample data (6 rows) cleanly. It also compiled cleanly - which is very unusual (that's a compliment; I use stringent warnings and even my code seldom compiles cleanly the first time). My amended version of your code is very similar to yours:
int main(void)
{
int i = 0;
int num;
contacts **contactArray = malloc(ELEMENTS * sizeof(contacts *));
/* allocate array */
for (i = 0; i < ELEMENTS; i++)
contactArray[i] = malloc(sizeof(contacts));
/* populate array */
for (i = 0; i < ELEMENTS; i++)
{
if (fgets(contactArray[i]->name,MAX,stdin) == 0 ||
fgets(contactArray[i]->street,MAX,stdin) == 0 ||
fgets(contactArray[i]->cityState,MAX,stdin) == 0 ||
fgets(contactArray[i]->zipCode,MAX,stdin) == 0)
break;
printf("%s", contactArray[i]->name);
printf("%s", contactArray[i]->street);
printf("%s", contactArray[i]->cityState);
printf("%s", contactArray[i]->zipCode);
}
printf("\n");
num = i;
qsort(contactArray, num, sizeof(contacts *), sortZips);
for (i = 0; i < num; i++)
{
fputs(contactArray[i]->name,stdout);
fputs(contactArray[i]->street,stdout);
fputs(contactArray[i]->cityState,stdout);
fputs(contactArray[i]->zipCode,stdout);
}
return 0;
}
The data I used was trivial repetitions of sets of 4 lines like this:
First LastName7
7 Some Street
City, CA
95437
Note that the 'error checking' I do in the input is the bare minimum that 'works'. If you get an over-long line in the input, one field will not contain a newline, and the next will contain the next section of the input line (possibly all the rest, possibly not - it depends on how badly overlong the line is).
If your addresses are printing out rubbish at the end, then it's almost certainly because you haven't allocated enough space for them. Twenty characters is a little on the low side for addresses.
What's probably happening is that you have an address like:
14237 Verylongstreetname Avenue
and, when you do fgets (street,20,stdin);, only 14237 Verylongstree will be read (19 characters, leaving space for the null terminator).
And, here's the crux: the file pointer will still be pointing at the tname Avenue bit so that, when you try to read the cityState, you'll get that. And, when you try to read the zipCode, you'll get the cityState line, effectively stuffing up your sorting.
I believe that you have enough space. Because you are using fgets and your size of MAX, the strings should be cut to fit and have a terminating NUL at the end.
Two things that might be messing it up:
fgets will read from where it stopped reading if the line is too long. That will result in an address of "This is too long so", " it will be cut\n". And then the rest of the input will be all over the place.
If you do not have enough input to fill ELEMENTS items then you'll get whatever random data was in the malloc'd memory. If you were to use calloc instead, it would zero the memory for you. Although the better idea would be to use a counter of how many items were actually read, instead of assuming there will be ELEMENTS items.

Resources