First, I'll explain why I'm doing this the way that I am. I'm taking a course in computer programming and my professor has given us an assignment where we have to make an array of records(each contains a first name, last name, & score), and then allow the user to manipulate the records using menu options. All of this MUST be done using only pointer arrays, and structures are not allowed. I know it is a headache. I know it probably one of the most difficult ways to accomplish this, but its what the professor wants.
With that out of the way, below is what I have for my main function so far. most of the long printf functions are just me printing debugging information. Please take note of the declaration of the char*** variable. It is meant to function as a 3D array where nameRecords[0] would be the first record, nameRecords[0][0] would be the first name of the first record, and nameRecords[0][1] is the last name of the first record. The third dimension is nameRecords[0][0][21], as the strings are only meant to be 20 characters long plus null character.
int main(void)
{
char ***nameRecords = NULL;
float *scores = NULL;
int size = 0; // total number of records
int usrInt = 0;
while(usrInt < 1)
{
printf("\nEnter the number of records to record(min 1): ");
scanf("%d", &usrInt);
inpurge();
if(usrInt < 1) printf("\nMust be integer greater than 1.\n");
}
nameRecords = (char***)calloc((size), sizeof(char**));
scores = (float*)calloc(size, sizeof(float));
int i;
for(i = 0; i < usrInt; i++)
{
addRecord(&nameRecords, &scores, &size);
printf("\nnameRecords#%p :: nameRecords[%d]#%p :: nameRecords[%d][0]=%s :: nameRecords[%d][1]=%s\n", nameRecords, size - 1, nameRecords[size - 1], size - 1, nameRecords[size - 1][0], size - 1, nameRecords[size - 1][1]);
}
printf("\nnameRecords[0]#%p\n", nameRecords[0]);
prntRecords(nameRecords, scores, size);
printf("\n\n\n");
return 0;
}
The trouble comes after I pass, for the SECOND TIME, &nameRecords into the addRecord function, defined below. To clarify, the segmentation fault is not received if the user chooses to enter only 1 entry at the beginning of the main function, and the program actually runs and terminates as expected.
void addRecord(char ****records, float **scores, int *size)
{
printf("\t(*records)[0]%p\n", (*records)[0]);
++*size; // increment total number of records by 1
int index = (*size) - 1;
char ***tempNames = (char***)realloc(*records, (*size) * sizeof(char**)); // reallocate larger space.
if(tempNames != *records)
*records = tempNames; // set original pointer to new value.
printf("\n\tsize - 1 = %d\n", index);
float *tempScores = (float*)realloc(*scores, (*size) * sizeof(float)); // reallocate larger space.
if(tempScores != *scores)
*scores = tempScores; // set original pointer to new value.
printf("\ttempNames[0]#%p\n", tempNames[0]);
tempNames[index] = (char**)calloc(tempNames[index], 2 * sizeof(char*));
enterRecord(tempNames[index], scores[index]);
printf("\n\ttempNames#%p :: tempNames[0]#%p :: tempNames[%d][0]=%s :: tempNames[%d][1]=%s\n", tempNames, tempNames[0], index, tempNames[index][0], index, tempNames[index][1]);
printf("\n\t*records#%p :: *records[0]#%p :: *records[%d][0]=%s :: *records[%d][1]=%s\n", *records, (*records)[0], index, (*records)[index][0], index, (*records)[index][1]);
return;
}
Below is an example output of the program. Without taking too long to explain whats happening, the tabbed lines are the lines of output from within the addRecord function. Specifically, the pointer to the first record, record[0], has been turned into a garbage value on the second pass through the addRecord function, just after the enterRecord function.
Enter the number of records to record(min 5): 2
(*records)[0](nil)
size - 1 = 0
tempNames[0]#(nil)
Enter first name: 1
Enter last name: 1
Enter score: 1
COMPLETE enterRecord
tempNames#0x6387010 :: tempNames[0]#0x6387050 :: tempNames[0][0]=1 :: tempNames[0][1]=1
*records#0x6387010 :: *records[0]#0x6387050 :: *records[0][0]=1 :: *records[0][1]=1
nameRecords#0x6387010 :: nameRecords[0]#0x6387050 :: nameRecords[0][0]=1 :: nameRecords[0][1]=1
(*records)[0]0x6387050
size - 1 = 1
tempNames[0]#0x6387050
Enter first name: 2
Enter last name: 2
Enter score: 2
COMPLETE enterRecord
tempNames#0x6387010 :: tempNames[0]#0x40000000 :: tempNames[1][0]=2 :: tempNames[1][1]=2
*records#0x6387010 :: *records[0]#0x40000000 :: *records[1][0]=2 :: *records[1][1]=2
nameRecords#0x6387010 :: nameRecords[1]#0x63870b0 :: nameRecords[1][0]=2 :: nameRecords[1][1]=2
nameRecords[0]#0x40000000
records#0x6387010 :: records[0]#0x40000000
Segmentation fault
All of the debug information points to the enterRecord function as being the culprit. So here it is, the evil enterRecord function...
void enterRecord(char **names, float *score)
{
names[0] = (char*)calloc(21, sizeof(char)); // allocate first name string
names[1] = (char*)calloc(21, sizeof(char)); // allocate last name string
printf("\nEnter first name: ");
fgets(names[0], 21, stdin);
if(strlen(names[0]) == 20) // IGNORE. just handles overflow from fgets.
inpurge();
remNewLine(names[0]); // removes '\n' character at end of string
printf("\nEnter last name: ");
fgets(names[1], 21, stdin);
if(strlen(names[1]) == 20) // IGNORE. just handles overflow from fgets.
inpurge();
remNewLine(names[1]); // removes '\n' character at end of string
printf("\nEnter score: ");
scanf("%f", score);
inpurge();
printf("\nCOMPLETE enterRecord\n");
return;
}
Only... no attempt at altering the affected pointer was made. The pointer value to the second element of the records array(records[1]) was passed into the function, and nothing I can see is altering the value of the pointer of the first element of the records array(records[0]), though the value of records[0] is what's causing the segfault.
I am very sorry for the length and all obfuscatory code. Again, this seems like a terrible approach to writing this program, but its what the situation calls for. I just feel bad for the poor teacher's aide who has to grade 30+ of these assignments.
Any help is welcomed.
this problem seems to be better implemented as
#define MAX_FIRST_NAME_LEN (21)
#define MAX_LAST_NAME_LEN (21)
#define MAX_SCORES (10)
// in file global memory...
static char **ppFirstNames = NULL;
static char **ppLastName = NULL;
static int **ppScores = NULL;
static int numOfEntries = 0;
// in the record input function, which needs NO parameters
scanf ( "%d", &numOfEntries );
if scanf fails, exit
ppFirstNames = malloc (numOfEntries*sizeof char*);
if malloc fails, exit
memset (ppFirstName, '\0', numOfEntries*sizeof char* );
ppLastName = malloc (numOfEntries*sizeof char*);
if malloc fails, free all, exit
memset (ppLastName, '\0', numOfEntries*sizeof char* );
ppScores = malloc (numOfEntries *sizeof int* );
if malloc fails, free all, exit
for(int i=0; i<numOfEntries; i++ )
ppFirstNames[i] = malloc( MAX_FIRST_NAME_LEN );
if malloc fails free all, exit
memset ( ppFirstNames[i], '\0', MAX_FIRST_NAME_LEN );
ppLastName[i] = malloc (MAX_LAST_NAME_LEN);
if malloc fails free all, exit
memset ( ppLastName[i], '\0', MAX_LAST_NAME_LEN );
ppScores[i] = malloc (MAX_SCORES *sizeof int);-1
if malloc fails, free all, exit
memset (ppScores[i], '\0', MAX_SCORES *sizeof int );
end for
for ( int i=0; i < numOfEntries; i++ )
now read each record
scanf( "%(MAX_FIRST_NAME_LEN-1)s", ppFirstNames[i] );
if scanf fails, free all, exit
scanf( "%(MAX_LAST_NAME_LEN-1)s", ppLastNames[i] );
if scanf fails, free all exit
for( int j=0; j< MAX_SCORES; j++ )
now read this students scores
int tempScore;
scanf( "%d", tempScore );
if scanf fails, free all, exit
if -1 == tempScore ) break;
ppScores[i][j] = tempScore;
end for
end for
The above is the pseudo code for inputting the records
and should be enough to get the input correct.
printing the info thereafter should be easy.
Example to use realloc for array multidimensional:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int size_initial = 10;
int **ptr;
int i, j;
ptr = (int**) malloc(sizeof (int*) * size_initial);
for (i = 0; i < size_initial; i++) {
ptr[i] = (int*) malloc(sizeof (int) * 10);
for (j = 0; j < 10; j++)
ptr[i][j] = i+j;
}
/* realloc +10 */
ptr = (int**) realloc(ptr, sizeof (int*) * (size_initial * 2));
for (i = size_initial; i < size_initial * 2; i++) {
ptr[i] = (int*) malloc(sizeof (int) * 10);
for (j = 0; j < 10; j++) {
ptr[i][j] = i+j;
}
}
/* print values */
for (i = 0; i < 20; i++) {
for (j = 0; j < 10; j++) {
printf("ptr[%d][%d] = %d\n", i, j, ptr[i][j]);
}
}
return 0;
}
Related
I'm trying to do a program that get number of names from the user, then it get the names from the user and save them in array in strings. After it, it sort the names in the array by abc and then print the names ordered. The program work good, but the problem is when I try to free the dynamic memory I defined.
Here is the code:
#include <stdio.h>
#include <string.h>
#define STR_LEN 51
void myFgets(char str[], int n);
void sortString(char** arr, int numberOfStrings);
int main(void)
{
int i = 0, numberOfFriends = 0, sizeOfMemory = 0;
char name[STR_LEN] = { 0 };
char** arrOfNames = (char*)malloc(sizeof(int) * sizeOfMemory);
printf("Enter number of friends: ");
scanf("%d", &numberOfFriends);
getchar();
for (i = 0; i < numberOfFriends; i++) // In this loop we save the names into the array.
{
printf("Enter name of friend %d: ", i + 1);
myFgets(name, STR_LEN); // Get the name from the user.
sizeOfMemory += 1;
arrOfNames = (char*)realloc(arrOfNames, sizeof(int) * sizeOfMemory); // Change the size of the memory to more place to pointer from the last time.
arrOfNames[i] = (char*)malloc(sizeof(char) * strlen(name) + 1); // Set dynamic size to the name.
*(arrOfNames[i]) = '\0'; // We remove the string in the currnet name.
strncat(arrOfNames[i], name, strlen(name) + 1); // Then, we save the name of the user into the string.
}
sortString(arrOfNames, numberOfFriends); // We use this function to sort the array.
for (i = 0; i < numberOfFriends; i++)
{
printf("Friend %d: %s\n", i + 1, arrOfNames[i]);
}
for (i = 0; i < numberOfFriends; i++)
{
free(arrOfNames[i]);
}
free(arrOfNames);
getchar();
return 0;
}
/*
Function will perform the fgets command and also remove the newline
that might be at the end of the string - a known issue with fgets.
input: the buffer to read into, the number of chars to read
*/
void myFgets(char str[], int n)
{
fgets(str, n, stdin);
str[strcspn(str, "\n")] = 0;
}
/*In this function we get array of strings and sort the array by abc.
Input: The array and the long.
Output: None*/
void sortString(char** arr, int numberOfStrings)
{
int i = 0, x = 0;
char tmp[STR_LEN] = { 0 };
for (i = 0; i < numberOfStrings; i++) // In this loop we run on all the indexes of the array. From the first string to the last.
{
for (x = i + 1; x < numberOfStrings; x++) // In this loop we run on the next indexes and check if is there smaller string than the currnet.
{
if (strcmp(arr[i], arr[x]) > 0) // If the original string is bigger than the currnet string.
{
strncat(tmp, arr[i], strlen(arr[i])); // Save the original string to temp string.
// Switch between the orginal to the smaller string.
arr[i][0] = '\0';
strncat(arr[i], arr[x], strlen(arr[x]));
arr[x][0] = '\0';
strncat(arr[x], tmp, strlen(tmp));
tmp[0] = '\0';
}
}
}
}
After the print of the names, when I want to free the names and the array, in the first try to free, I get an error of: "HEAP CORRUPTION DETECTED: after normal block(#87)". By the way, I get this error only when I enter 4 or more players. If I enter 3 or less players, the program work properly.
Why does that happen and what I should do to fix it?
First of all remove the unnecessary (and partly wrong) casts of the return value of malloc and realloc. In other words: replace (char*)malloc(... with malloc(..., and the same for realloc.
Then there is a big problem here: realloc(arrOfNames, sizeof(int) * sizeOfMemory) : you want to allocate an array of pointers not an array of int and the size of a pointer may or may not be the same as the size of an int. You need sizeof(char**) or rather the less error prone sizeof(*arrOfNames) here.
Furthermore this in too convoluted (but not actually wrong):
*(arrOfNames[i]) = '\0';
strncat(arrOfNames[i], name, strlen(name) + 1);
instead you can simply use this:
strcpy(arrOfNames[i], name);
Same thing in the sort function.
Keep your code simple.
But actually there are more problems in your sort function. You naively swap the contents of the strings (which by the way is inefficient), but the real problem is that if you copy a longer string, say "Walter" into a shorter one, say "Joe", you'll write beyond the end of the allocated memory for "Joe".
Instead of swapping the content of the strings just swap the pointers.
I suggest you take a pencil and a piece of paper and draw the pointers and the memory they point to.
I'm trying to get std input to scan in two 2d parallel arrays (arrAtk, arrDef) of x rows (x<100), and y columns (y<1,000,000). But y is a variable length in each row.
The first line of input is x for the number of rows in each array.
the second line is y for the number of columns in the first row.
Following that is y integers to be read into the arrAtk array.
Then another y integers to be read into the arrDef array.
Directly following is an int y for the number of columns in the next two rows.
And so on.
The parallel arrays will hold integers that will be sorted later, and each parallel element will be compared to see which of the rows had higher numbers.
Problem: So I'm trying to scan the input with a function call and dynamically allocate the correct amount of memory and scan the input for each row of the 2d arrays.
This seems to work okay but then when I try to print the array values in main it crashes. The printf statements work in the scanIn function so I must not be passing values correctly. How can I get it to where I can use the dynamically created arrays outside of the function?
Thanks in advance
Example of std input:
2 //<- x num of rows
2 //<- y num of cols
3
6
5
2
3 //<- y num of cols
2
3
12
9
3
4
CODE:
#include <stdio.h>
#include <stdlib.h>
int scanIn(int**,int**,int*);
int main(){
int cases, *armies, **arrAtk, **arrDef;
cases = scanIn(arrAtk,arrDef,armies);
printf("%d\n",arrAtk[1][2]); // Should be 12 with above input
printf("%d",arrDef[0][1]); // Should be 2
return 0;
}
int scanIn(int **arrAtk, int **arrDef, int *armies){
int i, j, cases;
scanf("%d",&cases);
arrAtk = (int**)malloc(sizeof(int*)*cases);
arrDef = (int**)malloc(sizeof(int*)*cases);
armies = (int*)malloc(sizeof(int)*cases);
for(i=0;i<cases;i++){
scanf("%d",&armies[i]);
arrAtk[i] = malloc(sizeof(int)*armies[i]);
arrDef[i] = malloc(sizeof(int)*armies[i]);
for(j=0;j<armies[i];j++){
scanf("%d",&arrAtk[i][j]);
}
for(j=0;j<armies[i];j++){
scanf("%d",&arrDef[i][j]);
}
}
return (cases);
}
While there are better ways of doing this, it can be done with the approach you have taken. The first thing to note is you were passing each pointer to your function instead of the address of the pointer. When that occurs, your function receives a copy of the pointer, containing the proper address for the values (if initialized), but with a very different address of its own.
So when you allocate storage for each of your arrays in the function, the pointers in main are completely unchanged. They still point to nothing. In order to have the allocations reflected in main you must pass the address of the pointer to your function, and dereference accordingly in your function, so that the allocations are available in main.
The short version is you need to call your function with scanIn (&arrAtk, &arrDef, &armies) and your prototype must be (int***, int***, int**). (not particularly attractive)
One other issue is that style in C generally avoids the use of caMelCase variables (leave that for C++). See: (section 2.2) NASA C Style Guide (Goddard Spaceflight Center 1994)
Below is an example of the additional level of indirection required to make the allocation work as you intended. (note: you should also free the memory you allocate):
#include <stdio.h>
#include <stdlib.h>
int scan_in (int***, int***, int**);
int main (void) {
int cases, *armies, **arr_atk, **arr_def;
cases = scan_in (&arr_atk, &arr_def, &armies);
printf ("\n cases : %d\n", cases);
printf (" arr_atk[1][2] : %d\n", arr_atk[1][2]);
printf (" arr_def[0][1] : %d\n\n", arr_def[0][1]);
return 0;
}
int scan_in (int ***arr_atk, int ***arr_def, int **armies)
{
int i, j, cases;
scanf ("%d",&cases);
*arr_atk = malloc (sizeof **arr_atk * cases);
*arr_def = malloc (sizeof **arr_def * cases);
*armies = malloc (sizeof *armies * cases);
for (i = 0; i < cases; i++) {
scanf ("%d", &(*armies)[i]);
(*arr_atk)[i] = malloc (sizeof ***arr_atk * (*armies)[i]);
(*arr_def)[i] = malloc (sizeof ***arr_def * (*armies)[i]);
for (j = 0; j < (*armies)[i]; j++) {
scanf ("%d", &(*arr_atk)[i][j]);
}
for (j = 0; j < (*armies)[i]; j++) {
scanf ("%d", &(*arr_def)[i][j]);
}
}
return (cases);
}
Input
$ cat ../dat/2dscan.txt
2
2
3
6
5
2
3
2
3
12
9
3
4
Output
$ ./bin/2dscanin < ../dat/2dscan.txt
cases : 2
arr_atk[1][2] : 12
arr_def[0][1] : 2
note: since you are new to C, there are a few more areas where you can improve your code: (1) always initialize your variables that are not explicitly assigned a value in your code; (2) always validate the return values from the functions you call; and (3) always keep track of, and free the memory you allocate when it is no longer needed. Taking that into consideration, your main and scan_in code would look like:
int main (void) {
int i, cases = 0, *armies = NULL, **arr_atk = {NULL}, **arr_def = {NULL};
if ((cases = scan_in (&arr_atk, &arr_def, &armies)) < 1) {
fprintf (stderr, "error: invalid value for cases returned.\n");
return 1;
}
printf ("\n cases : %d\n", cases);
printf (" arr_atk[1][2] : %d\n", arr_atk[1][2]);
printf (" arr_def[0][1] : %d\n\n", arr_def[0][1]);
for (i = 0; i < cases; i++) { /* free allocated memory */
if (arr_atk[i]) free (arr_atk[i]);
if (arr_def[i]) free (arr_def[i]);
}
if (arr_atk) free (arr_atk);
if (arr_def) free (arr_def);
if (armies) free (armies);
return 0;
}
int scan_in (int ***arr_atk, int ***arr_def, int **armies)
{
int i, j, cases;
if (scanf ("%d",&cases) != 1) {
fprintf (stderr, "scan_in() error: input failure.\n");
return 0;
}
*arr_atk = malloc (sizeof **arr_atk * cases);
*arr_def = malloc (sizeof **arr_def * cases);
*armies = malloc (sizeof *armies * cases);
for (i = 0; i < cases; i++) {
if (scanf ("%d", &(*armies)[i]) != 1) {
fprintf (stderr, "scan_in() error: input failure.\n");
return 0;
}
(*arr_atk)[i] = malloc (sizeof ***arr_atk * (*armies)[i]);
(*arr_def)[i] = malloc (sizeof ***arr_def * (*armies)[i]);
for (j = 0; j < (*armies)[i]; j++) {
if (scanf ("%d", &(*arr_atk)[i][j]) != 1) {
fprintf (stderr, "scan_in() error: input failure.\n");
return 0;
}
}
for (j = 0; j < (*armies)[i]; j++) {
if (scanf ("%d", &(*arr_def)[i][j]) != 1) {
fprintf (stderr, "scan_in() error: input failure.\n");
return 0;
}
}
}
return (cases);
}
I'm not sure why malloc is allocating so much space. Here's a snippet of the problem code:
char * hamming_string = NULL;
void enter_params(){
printf("Enter the max length: ");
scanf_s("%d", &max_length);
hamming_string = (char *) malloc(max_length * sizeof(char));
// to test what's going on with the hamming string
for(int i = 0; i < strlen(hamming_string); i++){
hamming_string[i] = 'a';
}
printf("hamming string = %s", hamming_string);
}
I set max_length to 2 and I'm seeing 12 a's. In another function, I was going to have the user input the hamming string using scanf_s("%s", &hamming_string); but I kept getting a access violation
hamming_string is not a string until one of its elements is a '\0'.
The str*() functions can only be used on strings.
Your program invokes Undefined Behaviour (by calling strlen() with something that is not a string).
malloc() allocates the amount of space that you ask for but it does not initialise it. When you call strlen() it scans the memory starting at what hamming_string points to and continues until it finds a null or it accesses memeory that it shouldn't and causes an exception.
In addition you need to allocate space for the null at the end of the string, if you want a string to hold 2 characters you need to allocate 3 characters to allow for the terminating null.
You are asking for the strlen of an uninitialized variable (this is undefined behaviour):
strlen(hamming_string);
(m)allocate one more in order to store the trailling \0:
hamming_string = malloc(max_length + 1);
change to
for(int i = 0; i < max_length; i++){
hamming_string[i] = 'a';
}
and don't forget to add the trailling \0 after the for loop:
hamming_string[i] = '\0'; /* or use calloc and skip this line */
void check_code(){
int actual_length, parity_bit, error_bit = 0, c = 0, i, j, k;
printf("Enter the Hamming code: ");
scanf_s("%s", &hamming_string);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This scanf_s() call is incorrect.
According to the C11 documentation or MSDN documentation it needs to be
scanf_s("%s", hamming_string, size - 1);
Note that you don't know size inside the function.
Note that you don't pass the address of hamming_string; hamming_string by itself gets converted to the address of its first element.
Example1:
char *hamming_string = malloc((max_length + 1) * sizeof(char));
for (i = 0; i < max_length; i++)
{
hamming_string[i] = 'a';
}
hamming_string[i] = '\0';
printf("hamming string = [%s]\n", hamming_string);
Output:
sdlcb#Goofy-Gen:~/AMD$ ./a.out
hamming string = [aaaaaaaaaaaa]
Example2:
char s;
for (i = 0; i < max_length; i++)
{
scanf(" %c", &s);
hamming_string[i] = s;
}
hamming_string[i] = '\0';
printf("hamming string = [%s]\n", hamming_string);
Output:
sdlcb#Goofy-Gen:~/AMD$ ./a.out
a
b
c
d
e
f
g
h
i
j
k
l
hamming string = [abcdefghijkl]
Basically, I'm trying to convert a bunch of char inputs to ints and assign them to a dynamic int array. The string input and tokenization seem to work fine. The issue (from what I can tell) seems to be with the reallocation of the int array; after the array is reallocated twice, the pointer to the int array returns NULL.
What I tried to do was double the size of the int array every time the number of tokens meets or surpasses (size divided by sizeof(int)). The realloc statement works each time this condition is met.
I thought using a pointer to a pointer was the end-all solution to this. I bet it's some really obvious issue, but I'm at my wit's end here. If you request any further elaboration, I'll try my best. Understand that I've only taken C for a semester and have struggled most of the way.
Also, truth be told, this was part of a class assignment which has since passed. I'd prefer an explanation about what's wrong more than a full-on code, if that's alright.
I have a lot of printf statements, so apologies for any clutter.
EDIT: Replaced all instances of newArray within the input() function with *resize. However, I've never tried assigning values through pointers to pointers, so feel free to correct me with a syntactic example if you know how I messed up. Segmentation fault occurs here:
for (k = (numElem - count); k < numElem; k++)
{
printf("\nk = %i\n", k);
printf("j = %i\n", j);
printf("numElem = %i\n", numElem);
printf("results[j]: %s\n\n\n", results[j]);
/* Segmentation fault regardless of what is assigned
to *resize[k]. */
*resize[k] = atoi(results[j]); // PROBLEM HERE
j++;
}
The source code has been updated to reflect upon this. To make this ridiculously long post a little more subdued, let's state that I did this in main():
int *newArray = malloc(MAXTOKEN * sizeof(int));
input(&newArray);
free(newArray);
Moving on.
/* String input takes in char values,
tokenizes them, converts the results
to int, assigns them to newresizeay. */
int input(int **resize)
{
int i, j, k, count;
int numElem = 0;
int currentSize = MAXTOKEN;
char str[MAXSTRING];
char *results[MAXTOKEN];
/* This entire loop takes place at least once,
provided the first input isn't NULL. */
do
{
i = 0, j = 0, k = 0;
/* Char input process. Takes place until the user
presses ENTER. */
printf("Input integer values separated by spaces, or "
"press ENTER to exit.\n");
while ( ((str[i] = getchar() ) != '\n') && (i < MAXSTRING) )
i++;
printf("\n\n");
str[i] = '\0';
/* Tokenization of the chars that were input */
count = 0;
if (results[0] = strtok(str, " \t"))
count++;
while (results[count] = strtok(NULL, " \t") )
count++;
/* numElem = 1 if the first input prompt established
str[0] as NULL */
if ( (count < 1) && (numElem < 1) )
count = 1;
numElem += count;
printf("numElem: %i\ncurrentSize: %i\n", numElem, currentSize);
/* If the number of elements to assign meet or surpass
the amount of [memory / sizeof(int)], exponentially
increase the size of the int resizeay. */
if ( numElem >= currentSize )
{
*resize = realloc(*resize, (currentSize) * sizeof(int));
if (*resize == NULL)
printf("\n\nYep, it threw up.\n\n");
currentSize *= 2;
}
printf("\nSize should be: %i\n", currentSize * 4);
printf("Actual size: %d\n", _msize(*resize));
/* The tokenized chars are converted to integers and
assigned to the int resizeay. */
for (k = (numElem - count); k < numElem; k++)
{
printf("\nk = %i\n", k);
printf("j = %i\n", j);
printf("numElem = %i\n", numElem);
printf("results[j]: %s\n\n\n", results[j]);
*resize[k] = atoi(results[j]); // PROBLEM HERE
j++;
}
for (i = 0; i < numElem; i++)
printf("resize[%i]: %i\n", i, *resize[i]);
printf("\n\n\n");
} while (str[0] != NULL);
}
The input function receives both resize and arr. main sends the same pointer to both. This is a bug.
When resize is resized, arr stays the same and may point to an invalid address (when realloc returns a different address).
How to fix:
Remove arr function argument and only use resize.
When you call the realloc function,if the new memory block is smaller than previous ,it will maintain the original state pointing to the memory block which previous used.If the new memory block is larger than previous,the system will re allocate memory on the heap and the previous memory is released.
Among other problems:
char *results[MAXTOKEN];
should be
char *results[MAXTOKEN + 1];
because here the maximum value of count will be MAXTOKEN in this loop :
while (results[count] = strtok(NULL, " \t") )
count++;
and
char str[MAXSTRING];
is pretty scary, because as soon as the user enters more than MAXSTRIN (=11) characters without pressing Enter, you will get a buffer overflow.
I have following code
int wordLenght = 256, arrayLength = 2, i = 0, counter = 0;
char **stringArray = NULL;
stringArray = calloc(arrayLength, sizeof(*stringArray));
for(counter; counter<wordLenght; counter++)
stringArray[counter] = calloc(wordLenght, sizeof(stringArray));
while(1)
{
printf("Input: ");
fgets(stringArray[i], wordLenght, stdin);
printf("stringArray[%d]: %s\n", i, stringArray[i]);
if(i == arrayLength)
{
printf("Reallocation !!!\n");
arrayLength *= 2;
stringArray = realloc(stringArray, arrayLength*sizeof(*stringArray));
}
i++;
}
I get this reallocation error:
*** glibc detected *** ./stringArray: realloc(): invalid next size: 0x0000000000b49010 ***
======= Backtrace: =========
/lib/libc.so.6(+0x775b6)[0x7f4dd12565b6]
/lib/libc.so.6(+0x7dd66)[0x7f4dd125cd66]
/lib/libc.so.6(realloc+0xf0)[0x7f4dd125d080]
./stringArray[0x4007f9]
/lib/libc.so.6(__libc_start_main+0xfd)[0x7f4dd11fdc4d]
./stringArray[0x400629]
What's my problem here ???
Thanks, greets
stringArray = calloc(arrayLength, sizeof(*stringArray));
Here you probably wanted to use sizeof(char*)
for(counter; counter<wordLenght; counter++) stringArray[counter] = calloc(wordLenght, sizeof(stringArray));
Here you are looping 256 times (wordLenght) but you should only 2 times (arrayLength). Additionally you probably wanted to use sizeof(char) instead of sizeof(stringArray).
if(i == arrayLength) {...}
This check should be done before you call fgets, because right now you are firstly using memory and later allocate them.
Additionally after you reallocate stringArray you need to allocate rest of strings using something like this
for(counter = i; counter<arrayLength; counter++) stringArray[counter] = (char*)calloc(wordLenght, sizeof(char));
And finally you need to free all allocated memory before you exit application.
You probably didn't mean sizeof(*stringArray)
In fact I believe you may want to relook at the calloc call too, I think you are allocating the size of the pointer there (word length times).
After the first time this line executes:
stringArray = realloc(stringArray, arrayLength*sizeof(*stringArray));
then stringArray[arrayLength/2] will be a garbage value - you haven't set it to point to storage for the word.
This part should use either use sizeof(**stringArray), or 1 as **stringArray is char, and the counter should only go up to arrayLength:
for(counter; counter<wordLenght; counter++)
stringArray[counter] = calloc(wordLenght, sizeof(stringArray));
Instead allocate in one block:
char* block = malloc(wordLength * arrayLength);
for ( counter; counter < arrayLength; ++counter )
stringArray[counter] = block + ( counter * wordLength );
At the moment, it's possible that there is some space after stringArray, into which you are storing the (wordLength-arrayLength) extra pointers when you calloc them, and realloc doesn't move stringArray.
It's quite probable that 0xb49010 is one of the pointers you calloc'd, and you're overwritten the memory where malloc keeps its block size..
But since you're writing off the end of stringArray, you're into undefined behaviour anyway.
Ok here is the whole solution:
int wordLength = 256, arrayLength = 2, i = 0, counter = 0;
char **stringArray = NULL;
char buffer[wordLength];
stringArray = calloc(arrayLength, sizeof(char*));
for(counter; counter<arrayLength; counter++) stringArray[counter] = (char*)calloc(wordLength, sizeof(char));
while(1)
{
if(i == arrayLength)
{
printf("Reallocation !!!\n");
arrayLength *= 2;
stringArray = realloc(stringArray, arrayLength*sizeof(char*));
for(counter = i; counter<arrayLength; counter++) stringArray[counter] = (char*)calloc(wordLength, sizeof(char));
}
printf("Input: ");
fgets(buffer, wordLength, stdin);
if(!strcmp(buffer,"q\n")) break; // also free here
else stringArray[i] = buffer;
printf("stringArray[%d]: %s\n", i, stringArray[i]);
i++;
}
How is the best way to free the space ?!