Consider the following functions
void alloco(int **ppa)
{
int i;
printf("inside alloco %d\n",ppa); /*this function allocates and fills 20 * sizeof(int) bytes */
*ppa = (int *)malloc(20 * sizeof(int));
/*fill all 20 * sizeof(int) bytes */
}
int main()
{
int *app = NULL;
int i;
printf("inside main\n");
alloco(&app);
for(i=0;i<20;i++) /*ISSUE::how will i know to traverse only 20 indexes?*/
printf("app[%d] = %d \n", i, app[i]);
return(0);
}
Basically how will main() come to know number of bytes to traverse i.e memory allocated by alloco() function. Is there any delimiter like NULL in character arrays?
That is not possible, you need to keep that value somewhere, for example you could do this,
void alloco(int **ppa, int count)
{
int i;
printf("inside alloco %d\n",ppa);
*ppa = malloc(count * sizeof(int));
if (*ppa == NULL)
return;
for (i = 0 ; i < count ; ++i)
/* fill it here. */
}
int main()
{
int *app;
int i;
int count;
count = 20;
app = NULL;
printf("Inside main\n");
alloco(&app, count);
if (app == NULL)
return -1;
for (i = 0 ; i < count ; i++)
printf("app[%d] = %d \n", i, app[i]);
/* done with `app' */
free(app);
return 0;
}
Many other combinations could work, for example
int alloco(int **ppa)
{
int i;
printf("inside alloco %d\n",ppa);
*ppa = malloc(20 * sizeof(int));
if (*ppa == NULL)
return;
for (i = 0 ; i < count ; ++i)
/* fill it here. */
return 20;
}
int main()
{
int *app;
int i;
int count;
printf("Inside main\n");
app = NULL;
count = alloco(&app);
if (app == NULL)
return -1;
for (i = 0 ; i < count ; i++)
printf("app[%d] = %d \n", i, app[i]);
/* done with `app' */
free(app);
return 0;
}
But I personally don't like this because if there is going to be a fixed number of integers it's not a good idea to use malloc() just,
int main()
{
int app[20];
int i;
printf("Inside main\n");
for (i = 0 ; i < sizeof(app) / sizeof(app[0]) ; i++)
printf("app[%d] = %d \n", i, app[i]);
return 0;
}
Is there any delimiter like NULL in character arrays?
If you define one, yes.
This however only is possible if your use case does not need all possible integer values.
If for example you would only need positive values including 0 you can define the value of -1 to be the "End-of-Array" marker.
You then would allocate one more element to the array then you need and assign -1 to this additional very last array element.
Example:
#include <stdlib.h> /* for malloc */
#include <errno.h> /* for errno */
#define EOA (-1)
int array_allocate(int ** ppi)
{
int result = 0;
if (NULL = ppi)
{
result = -1;
errno = EINVAL;
}
else
{
size_t number_of_elements = ...; /* Assign some positive value here. */
*ppi = malloc((number_of_elements + 1) * sizeof ** ppi);
if (NULL == *ppi)
{
result = -1;
}
else
{
(*ppi)[number_of_elements] = EOA;
}
}
return result;
}
ssize_t array_number_of_elements(int * pi)
{
int result = 0;
if (NULL == pi)
{
result = -1;
errno = EINVAL;
}
else
{
int * pi_tmp = pi;
while (EOA != *pi_tmp)
{
++pi_tmp;
}
result = pi_tmp - pi;
}
return result;
}
Use it like this:
#include <stdlib.h> /* for size_t and ssize_t */
#include <stdio.h> /* for printf and perror */
int array_allocate(int **);
ssize_t array_number_of_elements(int *);
int main(void)
{
int result = EXIT_SUCCESS;
int * pi = NULL;
if (-1 == array_allocate(&pi))
{
result = EXIT_FAILURE;
perror("array_allocate() failed");
}
else
{
ssize_t result_number_of_elements = array_number_of_elements(pi);
if (-1 == result_number_of_elements)
{
result = EXIT_FAILURE;
perror("array_number_of_elements() failed");
}
else
{
size_t number_of_elements = result_number_of_elements;
printf("The number of array's elements is %zu.\n",
number_of_elements);
}
}
free(pi); /* Clean up. */
return result;
}
It takes becoming a 3-Star Programmer
You can easily allocate some fixed number of elements (less than the maximum) in an function without passing the number of elements between the caller function and the callee. However, it takes creating an array of pointers to pointers to type How/Why? Essentially, you are treating your array as a null-terminated string, initially allocating all pointers to type within the array to NULL and only allocating space for them as needed. (allocating with calloc makes this a snap) When the array is used back in the caller, it allows iterating over all filled values until your reach the first null-pointer.
Now granted,
simply passing a pointer to size as an additional argument to your
function makes much more sense [1]
and eliminates the need for a triple-star rating, but for the purpose of example, enjoy being a 3-Star Programmer for a while:
#include <stdio.h>
#include <stdlib.h>
#define INITSZ 21
void alloco (int ***ppa)
{
printf("inside %s\n", __func__);
int i = 0;
/* allocate 21 pointers-to-int */
if (!(*ppa = calloc (INITSZ, sizeof **ppa))) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
/* allocate/fill 20 values (or anything less than 21) */
for (i = 0; i < INITSZ - 1; i++) {
if (!((*ppa)[i] = calloc (1, sizeof ***ppa))) {
fprintf (stderr, "%s() error: virtual memory exhausted.\n", __func__);
exit (EXIT_FAILURE);
}
*((*ppa)[i]) = i * 2;
}
}
int main()
{
int **app = NULL;
int i = 0;
printf ("inside main\n");
alloco (&app);
/*ISSUE::how will i know to traverse only 20 indexes?*/
while (app[i]) {
printf("app[%d] = %d \n", i, *(app[i]));
i++;
}
return(0);
}
Use/Output
$ ./bin/alloc_array+1
inside main
inside alloco
app[0] = 0
app[1] = 2
app[2] = 4
app[3] = 6
app[4] = 8
app[5] = 10
app[6] = 12
app[7] = 14
app[8] = 16
app[9] = 18
app[10] = 20
app[11] = 22
app[12] = 24
app[13] = 26
app[14] = 28
app[15] = 30
app[16] = 32
app[17] = 34
app[18] = 36
app[19] = 38
footnote [1]: emphasis added to the quote for clarity that this solution was intended to show what was possible, not what was most efficient or most practical.
Related
I attempt to malloc char** to store string, and free this, but I got this error. I can't understand why. The steps are as follows:
1:
char **pid_array = (char **)malloc(sizeof(char *) * MAX_LEN);
2:
pid_array[0] = (char *)malloc(sizeof(char) * SINGLE_LEN * MAX_LEN);
3:
free(pid_array); free(pid_array[0]);
The detailed code follows:
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_LEN 1000
#define SINGLE_LEN 10
int isPid(char *str) {
int len = strlen(str);
for (int i = 0; i < len; i++) {
if (isdigit(str[i]) == 0) {
return 1;
}
}
return 0;
}
void getFileName(char *dir_path, char *pid_array[], int *len) {
DIR *dir = opendir(dir_path);
if (dir == NULL) {
fprintf(stderr, "path open failed!\n");
exit(EXIT_FAILURE);
}
chdir(dir_path);
struct dirent *ent;
int i = 0;
while ((ent = readdir(dir)) != NULL) {
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {
continue;
}
int size = strlen(ent->d_name);
if (isPid(ent->d_name) == 0) {
pid_array[i++] = ent->d_name;
}
}
*len = i;
closedir(dir);
}
int main(int argc, char *argv[]) {
int pflag, nflag, vflag;
pflag = 0;
nflag = 0;
vflag = 0;
int opt;
while ((opt = getopt(argc, argv, "pvn")) != -1) {
switch (opt) {
case 'p':
pflag = 1;
break;
case 'v':
vflag = 1;
break;
case 'n':
nflag = 1;
break;
}
}
printf("pflag=%d; nflag=%d; vflag=%d; optind=%d\n", pflag, nflag, vflag, optind);
char **pid_array = (char **)malloc(sizeof(char *) * MAX_LEN);
pid_array[0] = (char *)malloc(sizeof(char) * SINGLE_LEN * MAX_LEN);
for(int i=0; i < MAX_LEN; i++){
pid_array[i]=pid_array[i-1]+SINGLE_LEN;
}
/*
for (int i = 0; i < MAX_LEN; i++) {
pid_array[i] = (char *)malloc(sizeof(char) * SINGLE_LEN);
assert(pid_array[i] != NULL);
}
*/
for (int i = 0; i < MAX_LEN; i++) {
free(pid_array[i]);
}
int *pid_array_len = (int *)malloc(sizeof(int));
getFileName("/proc", pid_array, pid_array_len);
for (int i = 0; i < *pid_array_len; i++) {
printf("%d\n", atoi(pid_array[i]));
}
free(pid_array);
free(pid_array[0]);
free(pid_array_len);
return 0;
}
The error is follow:
error
The steps as noted are not correct.
if pid_array is char** then
*pid_array is char*
**pid_array is char
And you need to construct them as such. And free them in the reverse order. If you intend to have a vector of pointers at pid_array then your case is very very common: every C program gets one for free. The main prototype can be declared as
int main(int argc, char**argv);
The system knows how many char* to pass to the program, but in your case maybe the simplest (safest) way is to use encapsulation and build a block like this
typedef struct
{
size_t argc;
char** argv;
} Block;
I will let an example below.
a way to free the block properly
If you insist in using just the pointer you can easily adapt this. Anyway a possible implementation is
Block* delete (Block* blk)
{
if (blk == NULL) return NULL;
fprintf(
stderr, "Deleting block of %llu strings\n",
blk->argc);
for (int i = 0; i < blk->argc; i += 1)
free(blk->argv[i]);
free(blk->argv);
free(blk);
fprintf(stderr, "Deleted...\n");
return NULL;
}
The reason to return a pointer is to create a simple way to assure the pointer is invalidated as in
my_block = delete (my_block);
In the example
A block is created
is filled with strings of random size
the strings are printed
the block is deleted
main for the example
int main(void)
{
srand(220630);
const int size = MAX_LEN;
Block* my_block = build(size);
fill(my_block);
show(my_block, "a vector of numbered strings");
my_block = delete (my_block);
return 0;
}
the output
a vector of numbered strings
25 strings:
1 "#000#k"
2 "#001#swfsxji"
3 "#002#cn"
4 "#003#akmxhksqgb"
5 "#004#dqnegzryobmhucldx"
6 "#005#iiuqddvuvukkrs"
7 "#006#jxvlsolocgnvgjcrwh"
8 "#007#zylbzumyhmeswxuno"
9 "#008#ex"
10 "#009#ixinxqyxqydnswb"
11 "#010#ylxelydzqgs"
12 "#011#absdfpdjvgwhxcmzekr"
13 "#012#sceqzvmjskkrmszpth"
14 "#013#n"
15 "#014#rsmkrqhssjniqgphjp"
16 "#015#dgojvpflydevwudvv"
17 "#016#qbmaolgrskkqghhkgb"
18 "#017#uzsunopqpdawg"
19 "#018#rvdeaiooylywf"
20 "#019#zfejmgqxu"
21 "#020#fjubcmllylxqahvbfh"
22 "#021#zwanyivra"
23 "#022#vooropiugmuya"
24 "#023#js"
25 "#024#qzecia"
Deleting block of 25 strings
Deleted...
The complete C code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN 25
typedef struct
{
size_t argc;
char** argv;
} Block;
Block* build(size_t ttl);
Block* delete (Block* blk);
int fill(Block* bl);
int show(Block* blk, const char* title);
int main(void)
{
srand(220630);
const int size = MAX_LEN;
Block* my_block = build(size);
fill(my_block);
show(my_block, "a vector of numbered strings");
my_block = delete (my_block);
return 0;
}
Block* build(size_t ttl)
{
if (ttl == 0) return NULL;
Block* blk = (Block*)malloc(sizeof(Block));
if (blk == NULL) return NULL;
blk->argc = (ttl > MAX_LEN) ? MAX_LEN : ttl;
blk->argv = (char**)malloc(ttl * sizeof(char*));
if (blk->argv == NULL) return NULL;
for (int i = 0; i < ttl; i += 1)
*(blk->argv + i) = NULL;
return blk;
}
int fill(Block* bl)
{
const char prefix[] = "#nnn#"; // common prefix
char buffer[30] = {0};
char data[20] = {0};
for (int i = 0; i < bl->argc; i += 1)
{
int rest = 1 + rand() % 19;
for (int j = 0; j < rest; j += 1)
data[j] = 'a' + rand() % 26; // a single letter
data[rest] = 0; // terminates string
int res = sprintf(buffer, "#%03d#%s", i, data);
bl->argv[i] = (char*)malloc(strlen(buffer) + 1);
strcpy(bl->argv[i], buffer);
}
return 0;
}
int show(Block* blk, const char* title)
{
if (title != NULL) printf("%s\n", title);
printf("%llu strings:\n", blk->argc);
for (int i = 0; i < MAX_LEN; i += 1)
printf("%d\t \"%s\"\n", 1 + i, *(blk->argv + i));
printf("\n");
return 0;
}
Block* delete (Block* blk)
{
if (blk == NULL) return NULL;
fprintf(
stderr, "Deleting block of %llu strings\n",
blk->argc);
for (int i = 0; i < blk->argc; i += 1)
free(blk->argv[i]);
free(blk->argv);
free(blk);
fprintf(stderr, "Deleted...\n");
return NULL;
}
// https://stackoverflow.com/questions/72809939/
// how-do-i-use-free-properly-to-free-memory-when
// -using-malloc-for-char
I've been using qsort to sort my arraylist in C (custom implementation)by character, but it returns a seg-fault & I am not quite sure why.
It seems like it is trying to access a pointer to an element that has already been removed.
Literal initialization:
Literal *newLiteralStruct (char *name, int i){
Literal *this = malloc(sizeof(Literal));
this->name = name;
this->isNegative = i;
return this;
}
Sort function:
int sort(const void *struct1, const void *struct2){
Literal* literalStruct1 = *(Literal**)struct1;
Literal* literalStruct2 = *(Literal**)struct2;
return strcmp(literalStruct1->name, literalStruct2->name);
}
Method call:
{
printf("Moving onto propagation..\n");
Literal *literal = findNextUnitLiteral(allClauses);
appendArrayList(unitLiterals, literal);
checkForLiteralsInClauses(literal, allClauses);
}
while (findNextUnitLiteral(allClauses) != NULL);
}
unsigned int arraySize = size(unitLiterals);
qsort((void*)unitLiterals, arraySize, sizeof(Literal*), &sort);
for (unsigned int i = 0; i < size(unitLiterals); i++){
Literal *literal = get(unitLiterals, i);
printf("%s", literal->name);
}
Full function that may cause issues:
void checkForLiteralsInClauses(Literal *literal, arrayList *clauseArray){
Literal *literalToCheck;
printf("Literal being propagated: ");
printCharacterAndSign(literal);
unsigned int clauseArraySize = size(clauseArray);
for (unsigned int i = 0; i<clauseArraySize; i++){
arrayList * literalArray = get(clauseArray, i);
unsigned int literalArraySize = size(literalArray);
for (unsigned int j = 0; j < literalArraySize; j++){
literalToCheck = get(literalArray, j);
if (literalToCheck == NULL){
break;
}
if ((strcmp(literalToCheck->name, literal->name)==0) && literalToCheck->isNegative != literal->isNegative){
printf("Literal identified: ");
printCharacterAndSign(literalToCheck);
printf("This is a converse literal. The literal in the clause will now be removed.\n");
removeFromArrayList(literalArray, j);
if (size(literalArray) == 0){
printf("contradiction");
printf("-");
exit(EXIT_SUCCESS);
}
else if (size(literalArray) == 1){
printf("This is now a unit literal");
appendArrayList(clauseArray, literalArray);
}
}
else if ((strcmp(literalToCheck->name, literal->name)==0 && literalToCheck->isNegative == literal->isNegative)){
printf("Literal identified: ");
printCharacterAndSign(literalToCheck);
printf("Same literal. The full clause will now be removed.\n");
removeFromArrayList(literalArray, j);
// destroyArrayList(literalArray);
removeFromArrayList(clauseArray, i--);
if (literalToCheck == 0){
break;
}
}
else if (strcmp(literalToCheck->name, literal->name) != 0){
printf("Literal identified: ");
printCharacterAndSign(literalToCheck);
printf("No match. Moving on!\n");
}
}
}
}
ArrayList Implementation:
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "ArrayList.h"
#define initialCapacity 10
/*
defining a function that copies (length) number of bytes from the pointer of the source to the pointer of the destination
mainly used for removing functions
*/
#define arrayListShift(s, offset, length) memmove((s)+offset, (s), (length)*sizeof(s));
/**
creating a new arrayList as defined in arraylist.h
allocates memory for the initialCapacity of the array, as specified above
initializes the size of the array as 0, as no elements have been added
the maximum capacity at the start is the pre-defined initial capacity
*/
arrayList *createNewArrayList(){
arrayList* arrayList = malloc(sizeof(arrayList));
arrayList->size = 0;
// Allocate the array
arrayList->element = malloc(sizeof(void*) * initialCapacity);
assert(arrayList->element);
arrayList->maxCapacity = initialCapacity;
return arrayList;
}
void appendArrayList(arrayList * arrayList, void * item ) {
if (arrayList->size == arrayList->maxCapacity){
arrayList->element = realloc(arrayList->element, 2*arrayList->maxCapacity * sizeof(void*));
arrayList->maxCapacity *= 1.5;
}
arrayList->element[arrayList->size++] = item;
}
/**
* remove an element from the array at a specified index position
* using memmove to shift all the following elements back by an index position
*/
void removeFromArrayList(arrayList *arrayList, unsigned int index){
unsigned int size = arrayList->size;
for (unsigned int i = index; i < (size-1); i++){
arrayList->element[i] = arrayList->element[i+1];
}
arrayList->size--;
}
void removeByPointer(arrayList*arrayList, void*pointer){
unsigned int num = arrayList->size;
for(unsigned int i = 0; i<num; i++){
if (arrayList->element[i] == pointer){
removeFromArrayList(arrayList, i);
break;
}
}
}
/**
* clear the array of all its elements
*/
void clearArrayList(arrayList*arrayList){
arrayList->size = 0;
}
/**
* return the size of the array
*/
unsigned int size(arrayList*arrayList){
return arrayList->size;
}
/**
* return the maxCap of the array
*/
unsigned int maxCapacity(arrayList*arrayList){
return arrayList->maxCapacity;
}
void *get(arrayList* arrayList, unsigned int index)
{
return arrayList->element[index];
}
/**
* free all the memory being used up by the arraylist
*/
void destroyArrayList(arrayList *arrayList){
free(arrayList->element);
free(arrayList);
}
I need to sort ints from a file in ascending order and print them to the standard output. I can't modify the structure of the file.
The txt file looks like this:
41
65
68
35
51
...(one number in a row)
My program works just fine for small files, but I have to optomize it for larger files (like 3 million numbers) using malloc, but don't know exactly where and how. I'd like to ask for help in this. (I'm a beginner)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER 100000
int sort(int size, int arr[])
{
for (int i = 0; i < size - 1; i++)
{
for (int j = 0; j < size - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int swap = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = swap;
}
}
}
}
int main(int argc, char *argv[])
{
char *filename = argv[1];
char s[20];
if (argc == 1)
{
fprintf(stderr, "Error! Input then name of a .txt file\n");
exit(1);
}
FILE *fp = fopen(filename, "r");
if (fp == NULL)
{
fprintf(stderr, "Error! Can't open %s\n", filename);
exit(1);
}
int arr[BUFFER];
int i = 0;
int size = 0;
while ((fgets(s, BUFFER, fp)) != NULL)
{
s[strlen(s) - 1] = '\0';
arr[i] = atoi(s);
++i;
++size;
}
fclose(fp);
sort(size, arr);
for (int i = 0; i < size; ++i)
{
printf("%d\n", arr[i]);
}
return 0;
}
Your program could look like this:
#include <stdlib.h>
#include <stdio.h>
static int numcompar(const void *a, const void *b) {
const int *x = a;
const int *y = b;
// it is tempting to return *x - *y; but undefined behavior lurks
return *x < *y ? -1 : *x == *y ? 0 : 1;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
// TODO: handle error
abort();
}
char *filename = argv[1];
// open the file
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
abort();
}
// this will be our array
// note realloc(NULL is equal to malloc()
int *arr = NULL;
size_t arrcnt = 0;
// note - I am using fscanf for simplicity
int temp = 0;
while (fscanf(fp, "%d", &temp) == 1) {
// note - reallocating the space each number for the next number
void *tmp = realloc(arr, sizeof(*arr) * (arrcnt + 1));
if (tmp == NULL) {
free(arr);
fclose(fp);
abort();
}
arr = tmp;
// finally assignment
arr[arrcnt] = temp;
arrcnt++;
}
fclose(fp);
// writing sorting algorithms is boring
qsort(arr, arrcnt, sizeof(*arr), numcompar);
for (size_t i = 0; i < arrcnt; ++i) {
printf("%d\n", arr[i]);
}
free(arr);
}
Note that reallocating for one int at a time is inefficient - realloc is usually a costly function. The next step would be to keep the number of the size of the array and "used" (assigned to) elements of the array separately and reallocate the array by a ratio greater then 1. There are voices that prefer to use the golden ratio number in such cases.
To read an undetermined number of entries from the input file, you can allocate and reallocate an array using realloc() as more entries are read. For better performance it is recommended to increase the allocated size by a multiple instead of increasing linearly, especially one entry at a time.
Your sorting routine is inappropriate for large arrays: insertion sort has quadratic time complexity, so it might take a long time for 3 million items, unless they are already sorted. Use qsort() with a simple comparison function for this.
Here is a modified program:
#include <stdio.h>
#include <stdlib.h>
static int compare_int(const void *pa, const void *pb) {
int a = *(const int *)pa;
int b = *(const int *)pb;
// return -1 if a < b, 0 if a == b and +1 if a > b
return (a > b) - (a < b);
}
int main(int argc, char *argv[]) {
if (argc == 1) {
fprintf(stderr, "Error! Input then name of a .txt file\n");
exit(1);
}
char *filename = argv[1];
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Error! Can't open %s\n", filename);
exit(1);
}
char buf[80];
size_t n = 0, size = 0;
int *array = NULL;
/* read the numbers */
while (fgets(buf, sizeof buf, fp)) {
if (n == size) {
/* increase size by at least 1.625 */
size_t newsize = size + size / 2 + size / 8 + 32;
int *newarray = realloc(array, newsize * sizeof(*array));
if (newarray == NULL) {
printf("cannot allocate space for %zu numbers\n", newsize);
free(array);
fclose(fp);
exit(1);
}
array = newarray;
size = newsize;
}
array[n++] = strtol(buf, NULL, 10);
}
fclose(fp);
/* sort the array */
qsort(array, n, sizeof(*array), compare_int);
for (size_t i = 0; i < n; i++) {
printf("%d\n", array[i]);
}
free(array);
return 0;
}
I'm allocating memory for my int *occurrences int *wordCounts and char **uniqueWords pointers and then at the end of the function that allocates the memory, i free them. However, when i compile the program i get an double free or corruption (!prev) aborting error. Is it caused by malloc,free or could it be due to how i initialize them inside the for loop ?
PS: I'm talking about the sortedCount() method, located towards the end
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define MAX_STRING_SIZE 512 /* each line in the file can have up to 512 chars */
void populateWordsArray(int);
void reverse(int);
void first(int);
void middle(int);
void last(int);
int count(int, char*, int);
void sortedCount(int);
void determineUniqueWords(int *,char **, int);
void *malloc_or_end(size_t);
void* malloc_or_end(size_t sz) {
void *pointer;
pointer = malloc(sz);
if(pointer == NULL) {
printf("Out of memory, terminating.\n");
exit(-1);
}
return pointer;
}
/* turn into local */
FILE *file;
char **wordList;
void determineUniqueWords(int *occurrences, char **word, int N) {
int i = 0;
int j = 0;
for(i = 0; i < N; i++) {
if(occurrences[i] < 1) {
continue;
}
for(j = i + 1; j < N; j++) {
if(occurrences[j] == 1 && (strcmp(word[i],word[j])) == 0) {
occurrences[i]++;
occurrences[j] = 0;
}
}
}
}
/**
* Function populateWordsArray: reads N words from
* the given file and populates the wordList array with them.
* Has one argument: int N - the number of words to read.
* */
void populateWordsArray(int N) {
int i = 0;
while(i < N && (fscanf(file,"%s",wordList[i]) == 1)) { /* fscanf returns the number of successfully read items. If it's not 1, the read failed. Same as checking if fscanf reads the eof char. */
i++;
}
}
/**
* Function reverse: prints the words of the
* text file in reverse order.
* */
void reverse(int N) {
int i = 0;
for(i = N-1; i >= 0; i--) {
if(i == 0) {
printf("%s \n",wordList[i]);
} else if(strcmp(wordList[i],"") == 0) { /* improve this in main-> memory allocation */
continue;
}else {
printf("%s ",wordList[i]);
}
}
return;
}
/**
* Function first: Prints the first char of each
* word in the file.
* */
void first(int N) {
char firstChar;
int i = 0;
for(i = 0; i < N; i++) {
firstChar = *wordList[i];
printf("%c",firstChar);
}
printf("\n");
return;
}
/**
* Function middle: Prints the middle char of each word
* from the given file.
* */
void middle(int N) {
int middleIndex = 0;
int i = 0;
char midChar;
for(i = 0; i < N; i++) {
if((strlen(wordList[i]) % 2) == 0) { /* artios */
middleIndex = ((strlen(wordList[i]) / 2) - 1);
midChar = wordList[i][middleIndex];
}
else { /* peritos */
middleIndex = (int) ceil((strlen(wordList[i]) / 2));
midChar = wordList[i][middleIndex];
}
printf("%c",midChar);
}
printf("\n");
return;
}
/**
* Function last: Prints the last char of each
* word from the given file.
* */
void last(int N) {
int i = 0;
char lastChar;
int lastPos;
for(i = 0; i < N; i++) {
lastPos = strlen(wordList[i]) - 1;
lastChar = wordList[i][lastPos];
printf("%c",lastChar);
}
printf("\n");
return;
}
/**
* Function count: Prints the number of times
* that the selected word is found inside the N first words
* of the file.
* */
int count(int N, char *word, int callID) {
int i = 0;
int count = 0;
for(i = 0; i < N; i++) {
if(strcmp(word,wordList[i]) == 0) {
count++;
}
}
if(callID == 0) { /* if callID == 0 (main called count and we want the output) */
printf("%d",count);
printf("\n");
}
return count;
}
void sortedCount(int N) {
int i,j = 0;
int *occurrences;
int *wordCounts;
char **uniqueWords;
/* mem allocation */
uniqueWords = malloc_or_end(N * sizeof(char*)); /* worst case: every word is unique */
wordCounts = malloc_or_end(N * sizeof(int));
occurrences = malloc_or_end(N * sizeof(int));
/* initialize rootWord and occurrences for the "each word is unique and occurs only once" scenario */
for(i = 0; i < N; i++) {
uniqueWords[i] = malloc_or_end(MAX_STRING_SIZE * sizeof(char));
occurrences[i] = 1;
}
determineUniqueWords(occurrences,wordList,N);
/* populate the wordCounts & uniqueWords "arrays" with the appropriate data in order to sort them successfully */
for(i = 0; i < N; i++) {
if(occurrences[i] > 0) {
wordCounts[i] = count(N,wordList[i],1);
uniqueWords[i] = wordList[i];
}
}
for(i = 0; i < N; i++) {
free(uniqueWords[i]);
}
free(uniqueWords);
free(occurrences);
free(wordCounts);
return;
}
int main(int argc,char *argv[]) { /* argv[1] = op argv[2] = name argv[3] = <word> */
int N = -1;
int i = 0;
int spaceNum,nlNum = -1;
file = fopen(argv[2],"r");
if(file == (FILE *) NULL) { /* check if the file opened successfully */
fprintf(stderr,"Cannot open file\n");
}
fscanf(file,"%d",&N); /* get the N number */
wordList = malloc_or_end(N * sizeof(char *)); /* allocate memory for pointers */
for(i = 0; i < N; i++) {
wordList[i] = malloc_or_end(MAX_STRING_SIZE * sizeof(char)); /* allocate memory for strings */
}
populateWordsArray(N);
if(strcmp(argv[1],"-reverse") == 0) {
reverse(N);
} else if(strcmp(argv[1],"-first") == 0) {
first(N);
} else if(strcmp(argv[1],"-middle") == 0) {
middle(N);
} else if(strcmp(argv[1],"-last") == 0) {
last(N);
} else if((strcmp(argv[1],"-count") == 0) && argv[3] != NULL) {
i = count(N,argv[3],0);
} else if((strcmp(argv[1],"-sorted") == 0) && (strcmp(argv[3],"-count") == 0)) {
sortedCount(N);
} else {
/* i only wish i could print something here */
}
/* End of program operations */
for(i = 0; i < N; i++) {
free(wordList[i]);
}
free(wordList);
fclose(file);
return 0;
}
You are overwriting the value of a pointer to heap memory on line 185:
uniqueWords[i] = wordList[i];
This means that when you free it later, you are actually freeing the allocated rows in wordList. Now you have two problems:
When you free the wordList rows on lines 244-246, it will be a double-free
You are losing your reference to the uniqueWords rows.
Use strcpy to assign to a dynamically-allocated string rather than the = operation.
This question already has answers here:
How C strings are allocated in memory?
(5 answers)
Closed 8 years ago.
I want to make dynamically allocated c-string table, but I think i don't understand the topic very well, could You explain it to me or correct my code?
#include <stdio.h>
int main()
{
int i;
char** t;
t=(char**)malloc(16*sizeof(char*));
for(i<0;i<100;i++)
{
*t[i]=(char*)malloc(16*sizeof(char));
}
return 0;
}
You should use variables for everything you want. For example, you allocate a memory for 16 elements of type char* and, as a result, you have an array with 16 elements, but then you are going from 0 to 100 ? Why ?
#include <stdio.h>
int main()
{
int n = 16;
int m = 16;
// Allocate a memory for n elements of type char*
char** t = (char**)malloc(n * sizeof(char*));
for(int i = 0; i < n; ++i)
{
// For each element in array t - initialize with another array.
// Allocate m elements of type char
t[i] = (char*)malloc(m * sizeof(char));
// Initialize
for(int j = 0; j < m; ++j)
t[i][m] = 'a';
}
// Print
for(int i = 0; i < n; ++i)
{
for(int j = 0; j < m; ++j)
printf("%c ", t[i][m]);
printf("\n");
}
// Free allocated memory!
for(int i = 0; i < n; ++i)
free(t[i]);
free(t);
return 0;
}
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
int main(int I__argC, char *I__argV[])
{
int rCode=0;
int i;
char **t=NULL;
size_t arraySize;
/* Parse command line args. */
if(2 != I__argC)
{
rCode=EINVAL;
printf("USAGE: %s {dynamic array size}\n", I__argV[0]);
goto CLEANUP;
}
arraySize=strtoul(I__argV[1], NULL, 0);
if(0 == arraySize)
{
rCode=EINVAL;
fprintf(stderr, "Cannot allocate a dynamic array of size zero.\n");
goto CLEANUP;
}
/* Allocate a dynamic array of string pointers. */
errno=0;
t = malloc(arraySize * sizeof(*t));
if(NULL == t)
{
rCode = errno ? errno : ENOMEM;
fprintf(stderr, "malloc() failed.\n");
goto CLEANUP;
}
memset(t, 0, arraySize * sizeof(*t));
/* Initialize each pointer with a dynamically allocated string. */
for(i=0; i<arraySize; i++)
{
errno=0;
t[i]=strdup("A string");
if(NULL == t[i])
{
rCode= errno ? errno : ENOMEM;
fprintf(stderr, "strdup() failed.\n");
goto CLEANUP;
}
}
CLEANUP:
/* Free the array of pointers, and all dynamically allocated strings. */
if(t)
{
for(i=0; i<arraySize; i++)
{
if(t[i])
free(t[i]);
}
free(t);
}
return(rCode);
}
Your code can be corrected so that it can create 100X16 character table or table with 100 string of 16 char length.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
int noOfStrings = 100;
int eachStringLenght = 16;
char** t;
t=(char**)malloc(noOfStrings*sizeof(char*));
for(i=0;i<noOfStrings;i++)
{
t[i]=(char*)malloc(eachStringLenght*sizeof(char));
}
return 0;
}