-4

ive written the following as a program in order to convert a pointer with a sequence of integers as its adress into an integer number:

#include <math.h>
#include <stdio.h>

char *recepcao() { char *receber;
    scanf("%s", receber);
    return receber;
}
int conversao(char *string) { int i, j;
    for(i=0; string[i]!='\0'; ++i) {
        continue;
    }
    int *var;
    int contagem=0;
    for(j=0; j<i-1; ++j) {
        var[j]=(string[j]-'0');
        contagem+=var[j]*pow(10, (i-j-1));
    }

    return contagem;
}
int main() {
    printf("%i", conversao(recepcao())); return 0;
}

I've tried about a thousand times to correct all the loops, but still after recieving the scanf value the program would crash, as stated on some IDEs, and would display a "Segmentation Fault: 11" message on others. How can I fix this? what is the proper definition of that message?

3 Answers3

1

receber is an uninitialized pointer. scanf expects a char* where the read characters will be written down. But in your case it points to some memory that you are not supposed to access. Trying to access it is undefined behavior. In your case it results in segmentation fault.

Among many some solutions would be to use char receber[MAXLEN+1] or char * receber = malloc(MAXLEN+1). There is a case here now. The first solution will result in an array which is of automatic storage duration - long story short, when the function ends it will point to some memory that will be invalid - so you can't return it (if you use first solution).

Second solution will allocate memory dynamically which will have storage duration beyond the scope of this function. This will be the correct one to use in this case.

For var in other function you can use dynamic memory allocation or VLA support if you have one. You should allocate memory of size equal to the length of the string. Here it doesnt stop at allocation - you need to initialize it with values so that you can use it in arithmetic operation like you did. (Using it uninitialized again is undefined behavior)

For your information there is a function named strlen which gives you the length of a string (nul terminated char array) - use it here.

Also if you check the reference manual or man pages you will see pow returns double - so here in case you will face some precision issues. For calculating integral power use custom functions - it is better in case of precision and avoiding nasty precision errors.

user2736738
  • 30,591
  • 5
  • 42
  • 56
  • You could also explain to the OP that `pow()` return is `double` – Michi Feb 25 '18 at 18:53
  • `contagem += var[j] * pow(10, (i-j-1));` the way how the code looks like, `var` is used uninitialized. – Michi Feb 25 '18 at 18:55
  • @Michi.: Yes OP has to initialize it and allocate. – user2736738 Feb 25 '18 at 18:55
  • There is no need to use a pointer there in the first place, even if it was an actual array or `malloc`ed pointer, the OP is not using the old values in any way. It would be better to just declare `char var`. – Pablo Feb 25 '18 at 19:02
  • 1
    @Pablo.: True..writing the whole thing (even this much) took me two cigar and all that . There is too many issues to consider. I chose the brutal ones only. – user2736738 Feb 25 '18 at 19:04
  • Yes, I tried to address all things that I've noticed in my address, hope I didn't miss anything. – Pablo Feb 25 '18 at 19:07
0

You are confusing pointers with arrays. A pointer just points; declaring a pointer does not reserve memory, it just creates a pointer variable pointing to some undefined place.

Corrected program:

char *recepcao(char *receber) {
    scanf("%s", receber);
    return receber;
}
int conversao(char *string) { int i, j;
    for(i=0; string[i]!='\0'; ++i) {
        continue;
    }
    int var[100];
    int contagem=0;
    for(j=0; j<i-1; ++j) {
        var[j]=(string[j]-'0');
        contagem+=var[j]*pow(10, (i-j-1));
    }

    return contagem;
}
int main() {
    char receber[100];
    printf("%i", conversao(recepcao(receber))); return 0;
}

Update:

Arrays and pointers are used in a very similar manner in C, but they are not the same. When you declare an array, the compiler reserves the required memory:

int a[10], b[10];
a[1] = 1;  // OK
a = b;     // Error!

Element of array is variable, but array name is not. Array name is sort of label, you cannot change its value so that it pointed to another array.

Pointer is variable. When you declare a pointer, the compiler creates it with undefined contents (like any other variable).

int *p, *q;
p = q;      // OK but useless because both the pointers contain junk addresses
a[0] = *p;  // Depending on where p points you will get either junk in a[0] or memory access violation
. . . 
p = a;                 // Now you can use p as a synonym for array a declared above
if (*(p + 1) == a[1])  // Always true
if (p[2] == *(a + 2))  // Always true
. . .
b = p;   // This won't work; can't assign a new value to array name b
. . .
q = (int*)malloc(sizeof(int) * 10); // Another way of creating int array of 10 elements
q = p;  // Array created with malloc is lost because nothing points to it now

Basically, the only difference between pointers and arrays is that pointer is variable and array name is not. You can't write "a = p" exactly as you can't write "2 = i".

There also is a rather confusing quirk with array as a formal argument:

   void xyz(int a[10]) {
       . . .
   }

   void main() {
       int b[20];
       . . .
       xyz(b);
   }

Looks like we assign b to a, and change array size, right? No, "int a[10]" is treated as "int *a", and sizeof(a) will return the size of the pointer, not the size of the array.

freim
  • 596
  • 2
  • 10
  • Right, I have fixed it. – freim Feb 25 '18 at 19:03
  • It's still not correct, `recepcao` can overflow the buffer and you don't check the return value of `scanf`. – Pablo Feb 25 '18 at 19:03
  • 1
    @freim Okay, you've fixed that problem, so I'm removing my first comment and my downvote, but there are still things here that can be improved, as Pablo's comment suggests. – Steve Summit Feb 25 '18 at 19:08
  • Ive read some more topics on pointers just to find out how much Im a c elephant dancing on eggs for now, but thanks for the patience though, you've been great. Ive been learning about half an hour a day through the internet before my classes start... – Pedro Secchi Feb 25 '18 at 19:29
  • @Steve Summit Yes, that's right, but I wanted to make as little change to the original program as possible, not to provide production-level code. The purpose was to explain the concept, and that required a certain level of simplification. – freim Feb 26 '18 at 08:55
  • @Pedro Secchi I have extended my answer with pointer vs. arrays explanation, hope you find it useful. – freim Feb 26 '18 at 09:51
0

Several problems here

char *recepcao() { 
    char *receber;
    scanf("%s", receber);
    return receber;
}

has 2 errors: receber is an uninitialized pointer, it's pointing to nowhere in particular, so passing to scanf is an undefined behaviour.

Also receber is a local variable of recepcao and it ceases to exists when recepcao returns, so you are returning a pointer to invalid memory location.

It would be better that you declare an array in main and pass that array (with its size) to the functions. I also would use fgets instead of scanf, it easier to control fgets.

You can use strlen(string) instead of

for(i=0; string[i]!='\0'; ++i) {
        continue;
}

but that's ok if you are not allowed to use strlen in your assignment.

Also using pow is overkill here, there is no need to include floating point arithmetic (pow returns a double) when you could use a variable exp initialized to 1 and multiply it by 10 in every loop (see my code below).

Also

int *var;
...
for(j=0; j<i-1; ++j) {
    var[j]=(string[j]-'0');
    ...

is not going to work because var is not initialized. There is no need to use a pointer here anyway, not even an array, you are not doing anything with the saved values. It would be better to use a single char variable:

char var;
...
for(j=0; j<i-1; ++j) {
    var = string[j] - '0';
    ...

So you can rewrite your program with this information like this:

#include <ctype.h>

char *recepcao(char *receber, size_t len) {
    if(fgets(receber, len, stdin) == NULL)
    {
        fprintf(stderr, "Could not read from user\n");
        return NULL;
    }

    // removing newline
    receber[strcspn(receber, "\n")] = 0;

    return receber;
}

int conversao(const char *string) { 
    if(string == NULL)
        return 0;

    size_t len = strlen(string);

    int res = 0, pow = 1;

    for(size_t i = len - 1; i >= 0; --i)
    {
        if(isdigit(string[i]))
        {
            res += (string[i] - '0') * pow;
            pow *= 10;
        } else {
            fprintf(stderr, "'%s' contains non-digits\n", string);
            return 0;
        }
    }

    return res;
}

int main(void)
{
    char line[100];

    if(recepcao(line, sizeof line) == NULL)
        return 1;

    printf("%d\n", conversao(line));

    return 0;
}
Pablo
  • 13,271
  • 4
  • 39
  • 59