There are a lot of questions about converting a 2D array into a 1D array, but I am attempting just the opposite. I'm trying to partition a string into substrings of constant length and house them in a 2D array. Each row of this 2D matrix should contain a substring of the initial string, and, if each row were to be read in succession and concatenated, the initial string should be reproduced.
I nearly have it working, but for some reason I am losing the first substring (partitions[0] -- length 8*blockSize) of the initial string (bin):
int main (void){
char* bin = "00011101010000100001111101001101000010110000111100000010000111110100111100010011010011100011110000011010";
int blockSize = 2; // block size in bytes
int numBlocks = strlen(bin)/(8*blockSize); // number of block to analyze
char** partitions = (char**)malloc((numBlocks+1)*sizeof(char)); // break text into block
for(int i = 0; i<numBlocks;++i){
partitions[i] = (char*)malloc((8*blockSize+1)*sizeof(char));
memcpy(partitions[i],&bin[8*i*blockSize],8*blockSize);
partitions[i][8*blockSize] = '\0';
printf("Printing partitions[%d]: %s\n", i, partitions[i]);
}
for(int j=0; j<numBlocks;++j)
printf("Printing partitions[%d]: %s\n", j,partitions[j]);
return 0;
}
The output is as follows:
Printing partitions[0]: 0001110101000010
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100
Printing partitions[0]: Hj
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100
The construction of partitions in the first for loop is successful. After construction at read out, the string at partitions[0] contains garbage values. Can anyone offer some insight?
int numBlocks = strlen(bin)/(8*blockSize); // number of block to analyze
char** partitions = (char**)malloc((numBlocks+1)*sizeof(char)); // break text into block
for(int i = 0; i<numBlocks;++i){
partitions[i] = (char*)malloc((8*blockSize+1)*sizeof(char));
memcpy(partitions[i],&bin[8*i*blockSize],8*blockSize);
partitions[i][8*blockSize] = '\0';
printf("Printing partitions[%d]: %s\n", i, partitions[i]);
}
This all looks suspicious to me; it's far too complex for the task, making it a prime suspect for errors.
For reasons explained in answers to this question, void * pointers which are returned by malloc and other functions shouldn't be casted.
There's no need to multiply by 1 (sizeof (char) is always 1 in C). In fact, in your first call to malloc you should be multiplying by sizeof (char *) (or better yet, sizeof *partitions, as in the example below), since that's the size of the type of element that partitions points at.
malloc might return NULL, resulting in undefined behaviour when you attempt to assign into the location it points at.
Anything else (i.e. everything that isn't NULL) that malloc, calloc or realloc returns will need to be freed when no longer in use, or else tools such as valgrind (a leak detection program, useful for people who habitually forget to free allocated objects and thus cause memory leaks) will report false positives and lose part of their usefulness.
numBlocks, i, or anything else that's for counting elements of an array, should be declared as a size_t to follow standard convention (e.g. check the strlen manual, synopsis section to see how strlen is declared, noting the type of the return value is size_t). Negative values caused by overflows here will obviously cause the program to misbehave.
I gather you've yet to think about any excess beyond the last group of 8 characters... This shouldn't be difficult to incorporate.
I suggest using a single allocation, such as:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLOCK_SIZE 8
int main(void) {
char const *bin = "00011101010000100001111101001101000010110000111100000010000111110100111100010011010011100011110000011010";
size_t bin_length = strlen(bin),
block_count = (bin_length / BLOCK_SIZE)
+ (bin_length % BLOCK_SIZE > 0); // excess as per point 6 above
char (*block)[BLOCK_SIZE + 1] = malloc(block_count * sizeof *block);
if (!block) { exit(EXIT_FAILURE); }
for (size_t x = 0; x < block_count; x++) {
snprintf(block[x], BLOCK_SIZE + 1, "%s", bin + x * BLOCK_SIZE);
printf("Printing partitions[%zu]: %s\n", x, block[x]);
}
for (size_t x = 0; x < block_count; x++) {
printf("Printing partitions[%zu]: %s\n", x, block[x]);
}
free(block);
exit(0);
}
Their are a few problems with your code.
You are allocating **partitions incorrectly.
Instead of:
char** partitions = (char**)malloc((numBlocks+1)*sizeof(char)); /* dont need +1, as numblocks is enough space. */
You need to allocate space for char* pointers, not char characters.
instead, this needs to be:
char** partitions = malloc((numBlocks+1)*sizeof(char*));
Also read Why not to cast result of malloc(), as it is not needed in C.
malloc() needs to be checked everytime, as it can return NULL when unsuccessful.
Once finished with the space allocated, it is always good to free() memory previously requested by malloc(). It is important to do this at some point in the program.
Here is some code which shows this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BLOCKSIZE 2
#define BLOCK_MULTIPLIER 8
int main(void) {
const char *bin = "00011101010000100001111101001101000010110000111100000010000111110100111100010011010011100011110000011010";
const size_t blocksize = BLOCKSIZE;
const size_t multiplier = BLOCK_MULTIPLIER;
const size_t numblocks = strlen(bin)/(multiplier * blocksize);
const size_t numbytes = multiplier * blocksize;
char **partitions = malloc(numblocks * sizeof(*partitions));
if (partitions == NULL) {
printf("Cannot allocate %zu spaces\n", numblocks);
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < numblocks; i++) {
partitions[i] = malloc(numbytes+1);
if (partitions[i] == NULL) {
printf("Cannot allocate %zu bytes for pointer\n", numbytes+1);
exit(EXIT_FAILURE);
}
memcpy(partitions[i], &bin[numbytes * i], numbytes);
partitions[i][numbytes] = '\0';
printf("Printing partitions[%zu]: %s\n", i, partitions[i]);
}
printf("\n");
for(size_t j = 0; j < numblocks; j++) {
printf("Printing partitions[%zu]: %s\n", j,partitions[j]);
free(partitions[j]);
partitions[j] = NULL;
}
free(partitions);
partitions = NULL;
return 0;
}
Which outputs non-garbage values:
Printing partitions[0]: 0001110101000010
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100
Printing partitions[0]: 0001110101000010
Printing partitions[1]: 0001111101001101
Printing partitions[2]: 0000101100001111
Printing partitions[3]: 0000001000011111
Printing partitions[4]: 0100111100010011
Printing partitions[5]: 0100111000111100
Related
I have a 2d pointer array:
char **fields = calloc(1, sizeof(char *));
I add to it different strings, like this:
if(i > 0) fields = realloc(fields, (i+1) * sizeof(char *));
fields[i] = calloc(size, sizeof(char));
I then use memcpy into the fields[i] the desired string.
At the end of the program, when I try to free fields, I do it like this:
int j=0
while(fields != NULL && fields[j]){
free(fields[j]);
j++;
}
free(fields);
The program inserts 4 strings into fields.
The first string frees as expected, however on the second iteration of the loop (j=1) the program stops and outputs the error: free(): invalid pointer
EDIT: I made a short program with the same problem:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
char **fields = calloc(1, sizeof(char *));
int fieldsIndex = 0,i=0;
while (i<4) {
if(fieldsIndex > 0){
fields = realloc(fields, (fieldsIndex + 1) * sizeof(char *));
fields[fieldsIndex] =NULL;
printf("amount of field places: %d\n", (fieldsIndex + 1));
}
fields[fieldsIndex] = calloc(8, sizeof(char));
fields[fieldsIndex] = "88888888";
fieldsIndex++;
i++;
}
int j=0;
for(j=0; j<i; j++){
printf("field: %s\n", fields[j]);
free(fields[j]);
}
free(fields);
return 0;
}
Can anyone help?
Addressing mainly the MRE.
The main problems are around this line:
fields[fieldsIndex] = "88888888";
It's not right for two reasons:
Firstly you need one more element in the array for the null byte.
Secondly, you make the fields[fieldsIndex] pointers point to string literals, it not only causes a memory leak, but also those string literals are usually stored in a readonly section of memory, either way the behavior freeing a pointer pointing to a string literal is undefined.
You need to copy the strings to the memory you just allocated. Using memcpy should work as long as you reserve enough memory as mentioned in the previous point, a cleaner way would be to use strdup.
Another issue is if(fieldsIndex > 0) because then fields[0] will not have allocated memory.
Some other notes, if you know the amount of strings (i < 4) you shouldn't need to realloc, just allocate space for all the pointers in the first calloc* (assuming that is not brought about by the construction of the MRE) , also i and fieldsIndex seem to be redundant.
Here is a demo keeping realloc (as it's tangential to the OP):
int main()
{
char **fields = NULL;
char **tempfields; // I advise the use of an auxiliary pointer for reallocation
int fieldsIndex = 0;
while (fieldsIndex < 4)
{
tempfields = realloc(fields, (fieldsIndex + 1) * sizeof *fields); //*
if (!tempfields)
{
// handle the allocation error appropriately
}
fields = tempfields;
printf("amount of field places: %d\n", (fieldsIndex + 1));
fields[fieldsIndex] = strdup("88888888");
// Or
// fields[fieldsIndex] = calloc(9, sizeof **fields); // check return
// strcpy(fields[fieldsIndex], "88888888");
fieldsIndex++;
}
// With int iterator
int j = 0;
for (j = 0; j < fieldsIndex; j++)
{
printf("field: %s\n", fields[j]);
free(fields[j]);
}
free(fields);
}
Or with a sentinel element in fields:
Live demo
// With sentinel
tempfields = realloc(fields, (fieldsIndex + 1) * sizeof *fields);
if (!tempfields)
{
// handle the allocation error appropriately
}
fields = tempfields;
fields[fieldsIndex] = NULL;
while (*fields)
{
printf("field: %s\n", *fields);
free(*fields);
fields++;
}
free(tempfields);
I have made the following code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * convertToBinary(int baseTenNumber)
{
int counter = 0;
char *result = NULL;
int remainder = baseTenNumber;
while(remainder)
{
result = (char *) realloc(result, sizeof(char) + 2);
printf("%d\n", baseTenNumber);
result[counter] = baseTenNumber%2;
remainder/=2;
counter++;
}
return result;
}
int main (void)
{
int input = -1;
while(input)
{
printf("Enter an integer: ");
scanf("%d", &input);
char * result = convertToBinary(input);
printf("The binary version of %d is %s\n", input, result);
memset(result, '0', sizeof(result));
free(result);
}
return 0;
}
Yes, I know the binaries will be written backwards, I will get to that later.
But why doesn't this program save my values?
I am trying to allocate memory on each iteration of the loop. I am not interested in doing it all in one go.
Any hints would be great, thanks!
There's a few ways to get an exact memory fit in C.
One is to precalculate exactly how much memory you need, which you could probably do here. But that's not always possible, and it's more code to write, and more bugs, and more runtime.
Another is to use realloc. Reallocating can get messy and expensive and it's a great way to invite bugs. Sometimes its necessary if you're working with an existing hunk of memory, like growing an array.
The other is to allocate a stack buffer large enough to hold any possible value, then use strdup to copy it to exactly the right amount of heap memory. This is great when you need to process something all at once and you're not sure how big it might be. For example, reading from a file.
// Allocate a big, reusable buffer
char line[BUFSIZ];
while( fgets(line, sizeof(line), fp) ){
...now you can slice and dice line...
}
We can do the same thing for your problem.
#include <stdio.h>
#include <string.h>
// The largest possible string to hold a binary integer
#define BINARY_INT_SIZE ((sizeof(int)*8) + 1)
// A cheap way to convert from integer 0/1 to char 0/1
const char BINARY_TO_CHAR[] = {'0', '1'};
char * convertToBinary(int number) {
// Initialize the stack variable to hold the conversion
char result[BINARY_INT_SIZE] = {0};
for( int counter = 0; number; counter++ ) {
printf("%d %d %d\n", counter, number, number % 2);
result[counter] = BINARY_TO_CHAR[number % 2];
number/=2;
}
// Copy from the stack into exactly enough heap memory
return strdup(result);
}
Stack memory is cheap and fast to allocate and discard. Much faster than reallocating heap memory over and over again in a loop. Using strdup guarantees the persisting heap memory is compact.
Just adding to the wonderful answers already presented, but I wanted to clarify some things no one explicitly stated, just to be safe.
For starters, your values are being saved (though not the correct amount, not in the right locations, and not the right values, I'll explain that later). Your printf() statement simply isn't recognizing the values stored in each index of the array you have made. In your format string, you are using the %s format specifier. This would be okay if you were actually printing a human-readable ASCII code. The problem is you are printing the literal values 0 and 1, which map to "NULL" and "Start of heading", respectively.
The other answers have very good suggestions for how to map the numeric values to the ASCII values, but in theory, if you wanted to print the the indexes in your array as-is, you would need to print in the following way:
printf("The binary version of %d is ", input);
for(int i = numDigits - 1; i >= 0; --i)
printf("%d", result[i]);
In this case, you are printing each item in the array as numbers as opposed to characters.
The next thing I want to point out is your logic in the while loop. Your assignment to result[counter] is always baseTenNum % 2, so it would either store all 0s or all 1s depending on whether the number is always even or odd. Then on top of that, your realloc() is always resizing to a fixed size, as opposed to growing with each new character (as pointed out in another answer :-)).
Hope these help clarify some of your troubles!
Schwerm's answer is the best.
If you want to allocate memory on each iteration, realloc(result, counter + 2) will allocate one more byte as counter increases. The +2 provides a byte for the current character and one for the terminator.
To get the 1 and 0 from the modulus operation into characters that can be printed, add '0'.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * convertToBinary(unsigned int baseTenNumber)
{
int counter = 0;
char *result = NULL;
char *temp = NULL;
int remainder = baseTenNumber;
while(remainder)
{
if ( NULL == ( temp = realloc(result, counter + 2))) {
fprintf ( stderr, "problem realloc\n");
exit ( 0);
}
result = temp;
printf("%d\n", remainder);
result[counter] = remainder%2 + '0';
remainder/=2;
counter++;
}
result[counter] = 0;//terminate
return result;
}
int main (void)
{
unsigned int input = 0;
do
{
printf("Enter an integer: ");
scanf("%u", &input);
if ( input) {
char * result = convertToBinary(input);
printf("The binary version of %d is %s\n", input, result);
free(result);
}
} while(input);
return 0;
}
I've read the other questions of this type, but I still can't figure out why this error is occurring in my code.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int len;
int str[0];
} bit_str;
bit_str * input(void);
void print_bitstr(bit_str * s);
int main(void)
{
bit_str * s1 = input();
print_bitstr(s1);
return 0;
}
bit_str * input(void)
{
bit_str *s = (bit_str *) malloc(sizeof(bit_str));
int count = 0, bit;
while ((bit = getchar()) != 'k'){
s = (bit_str *) realloc(s, sizeof(*s) + sizeof(int));
s->str[count++] = bit - 48;
}
s->len = count;
return s;
}
void print_bitstr(bit_str * s)
{
for (int i = 0; i < s->len; i++)
printf("%d ", s->str[i]);
printf("\n");
return;
}
This code is part of a program I was trying to add two bit strings together (as an exercise from a Data Structures book).
I've created a struct that will store the bits string and it's length. The struct is initialized using a malloc in the input function. It is reallocated every time a new bit is added. The bits are read using a getchar, and the end of a bitstring is demarcated by the letter 'k'.
When I compile the code, it works perfectly for exactly 6 bits. But if I try to input 7 bits, it crashes with following error:
> realloc(): invalid next size
I've read the other posts with similar error, but I couldn't figure out why this error is occurring in my code here. I've made sure that when I reallocate, I use sizeof operator to get the size, instead of absolute values.
Can someone please help me figure out what I'm doing wrong here?
sizeof(*s) is evaluated at compile-time; it has no knowledge of dynamic allocations such as malloc or realloc.
So even though you're attempting to dynamically add sizeof(int) bytes to the current allocation:
s = (bit_str *) realloc(s, sizeof(*s) + sizeof(int));
This always causes s to point to sizeof(bit_str) + sizeof(int) bytes.
Of course, after that no-op realloc, you proceed to write more bytes to the buffer, overrunning the buffer and resulting in undefined behavior.
I'm having a problem with memcpy() in c right now, and hope someone can help.
My program allows users to enter a string into a char pointer, and then calculates all possible permutations. As the permutations are generated (the user input pointer is changed into a permutation), the permutation is copied into a second char pointer via memcpy. It works perfectly, unless the string has two or more different repeating characters (eg. "CCBB" or "AADD"). If the user enters anything like this, memcpy (or even strcpy) causes the program to crash.
void Permute(char * word, char ** printPerm, int start, int end)
{
if (start == end)
{
memcpy(printPerm[permIndex], word, strlen(word) + 1);
++permIndex;
}
else
{
for (int i = start; i <= end; ++i)
{
Swap((word + start), (word + i));
Permute(word, printPerm, start + 1, end);
Swap((word + start), (word + i));
}
}
}
void Swap(char *a, char *b)
{
char temp;
temp = *a;
*a = *b;
*b = temp;
}
I've tried allocating more memory to both pointers but it's proved futile. Everything else works except for this.
Because I'm using gcc on Windows (MinGW), the details of my crash aren't shown. It simply says "perm.exe has stopped working". I used a series of printf() statements and found that the program is crashing on the memcpy() line.
A few details about the code:
The "word" char pointer holds the user's input. It will be morphed into permutations by the program, and it's contents will be dumped into "printPerm". "printPerm" the the char pointer array that holds the permutations, and will later be used to print the permutations when they've been sorted alphabetically and any duplicate entries have been removed. "permIndex" is the index of "printPerm" and is iterated every time a permutation is added to "printPerm".
Sorry I don't have more details, but using a text editor and gcc means I don't get much of a debugger. It seems any method of tranferring data between the pointers will crash the program ONLY if the string contains two or more different repeating characters.
You are lucky: my crystal ball just came back from repair! Let's see if it works now:
// ALL CHECKS OMMITTED!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int permIndex;
// place the two functions you published here
// Uhmmm...well...but we need one
int factorial(int n)
{
int f = 1;
do {
f *= n;
} while (--n);
return f;
}
int main(int argc, char **argv)
{
int f,i;
char **pperm;
char *word;
size_t length;
if (argc < 2) {
fprintf(stderr, "Usage: %s string\n", argv[0]);
exit(EXIT_FAILURE);
}
// work on copy
length = strlen(argv[1]);
word = malloc(length + 1);
strcpy(word, argv[1]);
// You either allocate memory as you need it but as you compute
// all combinations first, you need the whole memory for them
// at once. That means the amount of memory needed is known in
// advance and can be allocated at once
f = factorial((int) length);
// allocate memory for an array of (length)! pointers to char
pperm = malloc(f * sizeof(char *));
for (i = 0; i < f; i++) {
// allocate memory for the n characters plus '\0'
pperm[i] = malloc(length + 1);
}
Permute(word, pperm, 0, length - 1);
// do something with the list
// print it
for (i = 0; i < f; i++) {
printf("%s\n", pperm[i]);
// we don't need the memory anymore: free it
free(pperm[i]);
}
// free that array of pointers mjentioned above
free(pperm);
// free the memory for the input
free(word);
exit(EXIT_SUCCESS);
}
This is one of several ways to do it.
So i am trying to read a text file line by line and save each line into a char array.
From my printout in the loop I can tell it is counting the lines and the number of characters per line properly but I am having problems with strncpy. When I try to print the data array it only displays 2 strange characters. I have never worked with strncpy so I feel my issue may have something to do with null-termination.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *f = fopen("/home/tgarvin/yes", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos); fread(bytes, pos, 1, f);
int i = 0;
int counter = 0;
char* data[counter];
int length;
int len=strlen(data);
int start = 0;
int end = 0;
for(; i<pos; i++)
{
if(*(bytes+i)=='\n'){
end = i;
length=end-start;
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
printf("%d\n", counter);
printf("%d\n", length);
start=end+1;
counter=counter+1;
}
}
printf("%s\n", data);
return 0;
}
Your "data[]" array is declared as an array of pointers to characters of size 0. When you assign pointers to it there is no space for them. This could cause no end of trouble.
The simplest fix would be to make a pass over the array to determine the number of lines and then do something like "char **data = malloc(number_of_lines * sizeof(char *))". Then doing assignments of "data[counter]" will work.
You're right that strncpy() is a problem -- it won't '\0' terminate the string if it copies the maximum number of bytes. After the strncpy() add "data[counter][length ] = '\0';"
The printf() at the end is wrong. To print all the lines use "for (i = 0; i < counter; i++) printf("%s\n", data[counter]);"
Several instances of bad juju, the most pertinent one being:
int counter = 0;
char* data[counter];
You've just declared data as a variable-length array with zero elements. Despite their name, VLAs are not truly variable; you cannot change the length of the array after allocating it. So when you execute the lines
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
data[counter] is referring to memory you don't own, so you're invoking undefined behavior.
Since you don't know how many lines you're reading from the file beforehand, you need to create a structure that can be extended dynamically. Here's an example:
/**
* Initial allocation of data array (array of pointer to char)
*/
char **dataAlloc(size_t initialSize)
{
char **data= malloc(sizeof *data * initialSize);
return data;
}
/**
* Extend data array; each extension doubles the length
* of the array. If the extension succeeds, the function
* will return 1; if not, the function returns 0, and the
* values of data and length are unchanged.
*/
int dataExtend(char ***data, size_t *length)
{
int r = 0;
char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
if (tmp)
{
*length= 2 * *length;
*data = tmp;
r = 1;
}
return r;
}
Then in your main program, you would declare data as
char **data;
with a separate variable to track the size:
size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;
You would allocate the array as
data = dataAlloc(dataLength);
initially. Then in your loop, you would compare your counter against the current array size and extend the array when they compare equal, like so:
if (counter == dataLength)
{
if (!dataExtend(&data, &dataLength))
{
/* Could not extend data array; treat as a fatal error */
fprintf(stderr, "Could not extend data array; exiting\n");
exit(EXIT_FAILURE);
}
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
strncpy(data[counter], bytes+start, length);
data[counter][length] = 0; // add the 0 terminator
}
else
{
/* malloc failed; treat as a fatal error */
fprintf(stderr, "Could not allocate memory for string; exiting\n");
exit(EXIT_FAILURE);
}
counter++;
You are trying to print data with a format specifier %s, while your data is a array of pointer s to char.
Now talking about copying a string with giving size:
As far as I like it, I would suggest you to use
strlcpy() instead of strncpy()
size_t strlcpy( char *dst, const char *src, size_t siz);
as strncpy wont terminate the string with NULL,
strlcpy() solves this issue.
strings copied by strlcpy are always NULL terminated.
Allocate proper memory to the variable data[counter]. In your case counter is set to 0. Hence it will give segmentation fault if you try to access data[1] etc.
Declaring a variable like data[counter] is a bad practice. Even if counter changes in the subsequent flow of the program it wont be useful to allocate memory to the array data.
Hence use a double char pointer as stated above.
You can use your existing loop to find the number of lines first.
The last printf is wrong. You will be printing just the first line with it.
Iterate over the loop once you fix the above issue.
Change
int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
strncpy(data[counter], bytes+start, length);
...
to
int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
strncpy(data[counter], bytes+start, length);
...
//1: to prepare valid memory storage for pointers to lines (e.g. data[0] to data[MAX_DATA_LINES]). Without doing this, you may hit into 'segmentation fault' error, if you do not, you are lucky.
//2: Just to ensure that if the total number of lines in the file are < MAX_DATA_LINES. You do not run into 'segmentation fault' error, because the memory storage for pointer to line data[>MAX_DATA_LINES] is no more valid.
I think that this might be a quicker implementation as you won't have to copy the contents of all the strings from the bytes array to a secondary array. You will of course lose your '\n' characters though.
It also takes into account files that don't end with a new line character and as pos is defined as long the array index used for bytes[] and also the length should be long.
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_LINE_ARRAY_DIM 100
int main(int argc, char* argv[])
{
FILE *f = fopen("test.c", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
fread(bytes, pos, 1, f);
if (bytes[pos-1]!='\n')
{
bytes[pos++] = '\n';
}
long i;
long length = 0;
int counter = 0;
size_t size=DEFAULT_LINE_ARRAY_DIM;
char** data=malloc(size*sizeof(char*));
data[0]=bytes;
for(i=0; i<pos; i++)
{
if (bytes[i]=='\n') {
bytes[i]='\0';
counter++;
if (counter>=size) {
size+=DEFAULT_LINE_ARRAY_DIM;
data=realloc(data,size*sizeof(char*));
if (data==NULL) {
fprintf(stderr,"Couldn't allocate enough memory!\n");
exit(1);
}
}
data[counter]=&bytes[i+1];
length = data[counter] - data[counter - 1] - 1;
printf("%d\n", counter);
printf("%ld\n", length);
}
}
for (i=0;i<counter;i++)
printf("%s\n", data[i]);
return 0;
}