Problem in program executable created in c

1

Well, I'm currently facing a somewhat strange problem here with my program in c. I'm creating a program in c that le integers from an external text file (I use the standard library and the fscanf function) and store it in a dynamically allocated vector (I'm using the malloc and realloc functions). The program worked perfectly on two computers, both running direct from the IDE (I use codeblocks with gcc) and the generated .exe. The strange thing is that on the other two computers I tested, both work perfectly when I run in codeblocks, but in one when I run the generated .exe it does not read the data from the file and another one gives error. What could be happening? Thanks if anyone can help.

The code is a bit large, so I'll put some important functions into it.

Function that allocates data:

int leDados(DADOS *dado){
int i = 0;
unsigned long int tamanho = 0;
FILE *arq;
arq = fopen("dados/config.txt","r");
tamanho = sizeof(DADOS);
while(fscanf(arq, "%d", &dado[i].id) != EOF){
    tamanho += sizeof(DADOS);
    dado = realloc(dado, tamanho);
    i++;
}
fclose(arq);
return i;

}

    
asked by anonymous 25.07.2016 / 17:42

3 answers

0

Passing parameters by refining - with pointers, using * - is not magic! you need to understand what it does - In C we indicate that a function receives a pointer, with "*", practically just to make the program easier to read - depending on the configurations the compiler can give a warning or an error if we use this incorrectly - and depending on how wrong we use this, the compiler has nothing to do - nor can tell you. That's the case there.

So keep in mind that a pointer is a number. It indicates a position in the process memory. Let's suppose that your leDados is called with the dado vector at memory location "1000".

When you change something in the contents of this pointer - you want to change any existing given [i] value, this will work - and that's how the referral call is used.

However, you try to make a change in the pointer's own address! When you type dado = realloc(dado, tamanho); the value 1000 in data can remain the same, but only if realloc gets more memory space in the same block - but it can return another value - if it can not increase the space allocated at address 1000 to the requested size, the call can transfer its data to another address (for example, 2000) - and returns "2000" that is in the local variable dado . Referrals within the same function to vector elements will continue to work - why they will be based on the new address "2000" - but the function that called leDados only knows the address 1000 - (and leDados can not change that ). When returning from the read function, the program will try to read numbers in a position that is no longer allocated to the program data, and will give error.

The operation is intermittent, because if realloc can always expand the memory block at the starting address (1000 in our example), the caller leDados continues with a valid reference to the vector. But if realloc had in some call that move the block of data, the program is lost. And the factors for this call having to change position are innumerable - having to do with the internal workings of the system, and specific settings for the process profile. Anyway, even when this program works as it is, it's just by chance.

The simplest way to solve, since this function fills the whole vector, is to allocate the initial vector in itself, and it returns the new pointer. Use a default value, such as a sentinel value, for the rest of the program to know the size of the vector:

#define SENTINELA = 0x8fffffff
DADOS *leDados(){
   DADOS *dado;
   int i = 0;
   while (...){ ...; i++}
   dado[i] = SENTINELA
   return dado;
}

Another way is to pass the address where the "given" address is - so that the "leDados" function can change its own memory address so that it is also changed in the calling function:

void main() {
    DADOS * dado;
    leDados (&dado);
}
...
int leDados( DADOS **dado){
...
    while(fscanf(arq, "%d", &((*dado)[i].id)) != EOF){
       *dado = realloc(*dado, tamanho);
       ...
    }
    ...
    return i;
}

And last but not least : Unless you are doing exercises to learn better C, and wanting to improve your understanding of pointers - that is - if you are facing a real computing problem that you want to solve with this program on a personal computer it is time to learn another language and do not this program in C. (Another use case for real programs in C is if you are programming to run on a minicomputer, or microcontroller, such as arduino, or other IoT application)

In C you have to worry about everything: including allocating this data structure, and all the details of how you will manipulate it. In higher-level languages such as Ruby, Python, PHP, Javascript, there are data streams ready to use, with all the memory allocation, search of elements, etc. ... ready to use.

For example, to read this same file in a program in Python, putting all the numbers in a structure that the language already has memory, already knows the size, allows ordering, deletion, etc., you need to do:

dado = [int(n) for n in open(dados/config.txt").read().split()]

And then you just have to worry about using your numbers.

    
25.07.2016 / 20:32
0

Following your line of reasoning, a complete and tested solution to your problem follows:

#include <stdio.h>
#include <stdlib.h>


void exibir_numeros( int numeros[], int qtd )
{
    int i = 0;

    for( i = 0; i < qtd; i++ )
        printf("n[%d] = %d\n", i, numeros[i] );
}


int carregar_numeros( const char * arq, int * numeros[], int * qtd )
{
    FILE * pf = NULL;
    int n = 0;
    int i = 0;
    int * p = NULL;

    pf = fopen( arq, "r" );

    if(!pf)
        return -1;

    while( fscanf( pf, "%d", &n ) != EOF )
    {
        i++;
        p = realloc( p, i * sizeof(int) );
        p[ i - 1 ] = n;
    }

    fclose(pf);

    *qtd = i;
    *numeros = p;

    return 0;
}

/* fim-de-arquivo */

Compiling (Linux / GCC):

$ gcc -Wall numeros.c -o numeros

Input File:

1
2
3
100000
12345678
123456789
0987654321
1212121212
20000
30000
314215
6547438738
89234658725
-1000
-3000

Output:

$ ./numeros teste.txt
n[0] = 1
n[1] = 2
n[2] = 3
n[3] = 100000
n[4] = 12345678
n[5] = 123456789
n[6] = 987654321
n[7] = 1212121212
n[8] = 20000
n[9] = 30000
n[10] = 314215
n[11] = -2042495854
n[12] = -959654491
n[13] = -1000
n[14] = -3000

I hope I have helped!

    
25.07.2016 / 22:02
0

Only with what you put it is difficult to say where the error is, but I can point out some possible problems. What I really suggest is use a debugger . This will allow you to understand exactly what the computer is doing.

Come on:

int leDados(DADOS *dado){
    // dado pode ser NULL? vc sempre passa algo que já está alocado?

    int i = 0;
    unsigned long int tamanho = 0;
    FILE *arq;
    arq = fopen("dados/config.txt","r");
    // tem certeza que deu certo a abertura do arquivo? arq é um ponteiro 
    // válido?

    tamanho = sizeof(DADOS);

    // dado tem uma alocação válida aqui? lembre-se não vale se estiver 
    //na pilha, tem que estar no heap
    while(fscanf(arq, "%d", &dado[i].id) != EOF){
       tamanho += sizeof(DADOS);
       dado = realloc(dado, tamanho);
       // a reallocação deu certo? Vc sempre vai jogar fora uma
       // realocação? Pq, quando der EOF, uma realocação vai estar perdida
       i++;
    }
    fclose(arq);
    return i;
}

As a final comment, remember that every time you enter the loop, you make a relocation, this makes your program under-optimized. Relocation is a costly operation. If each line in your file matches an integer, it may be more interesting to count how many rows it has in your file, make a full allocation, and then populate it with values.

    
26.07.2016 / 04:49