0

I am trying to use sscanf (on a string that I know is well formed and not malicious) to write a value to a specific part of an array.

#include <stdio.h>
int main(void){
    int arr[10], i;
    sscanf("5 5", "%d %d", &i, arr + i);
}

When I run this in valgrind, I am told that the sscanf line reads an uninitialized value. However, arr is initialized as a pointer to an automatically allocated array and i is initialized because there is a sequence point associated with every format specifier (this is not the case for general functions which do not have sequence points between arguments). I might be inclined to believe that this is only happening because valgrind does not know about that format specifier sequence point rule, but in my full program the line causes an actual segmentation fault. Why is this happening? Am I mistaken about format specifiers creating sequence points?

hacatu
  • 638
  • 6
  • 15
  • 2
    The evaluation order of arguments of `scanf` and family is undefined, in your case `arr + uninitalized value of i` is evaluated first. – David Ranieri Mar 31 '16 at 18:31
  • 7
    @AlterMann: The order of evaluation of the function arguments is a red herring. The salient point is that the last argument is evaluated *before* the second argument is modified by the function call. *All* arguments are evaluated before the function is called. – EOF Mar 31 '16 at 18:34
  • @EOF, what does it mean "a red herring"? – David Ranieri Mar 31 '16 at 18:37
  • @AlterMann: A "red herring" is something that looks important, but is irrelevant. – EOF Mar 31 '16 at 18:40
  • @EOF. ah, what a nice expression :) – David Ranieri Mar 31 '16 at 18:42
  • 1
    @AlterMann A "red herring" is an English idiom that means "a false clue". (https://en.wikipedia.org/wiki/Red_herring) In this case, it means that "order of evaluation" is not applicable here. What happens is that the compiler calculates the address of the pointers for all arguments before reading in the data into those addresses. Thus the value i has not had anything scanned into it when the addresses are determined. It would not matter in which order the two arguments were written. – sabbahillel Mar 31 '16 at 18:43
  • @sabbahillel, got it, thanks ;) – David Ranieri Mar 31 '16 at 18:44
  • And the relevant part of C11 draft standard n1570: *6.5.2.2 Function calls 10 There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call.* – EOF Mar 31 '16 at 18:48
  • 4
    Instead of 2 reads, just use `int arr[10], i, j; sscanf("5 5", "%d, &d", &i, &j); arr[I]=j;` that way you can insert a safety check before the last line to ensure i is in range before referencing arr[i]. – FredK Mar 31 '16 at 18:51

1 Answers1

2

The problem is that the sequence point of the function call means that i is definitely accessed for the argument computation of arr + i before it is set by sscanf. So you are accessing it before it is initialized.

You'll need two sccanf calls; something like:

int len, i;

if (sscanf(buf, "%d%n", &i, &len) >= 1 && sscanf(buf+len, "%d", arr + i) >= 1) {
    // successful sscanf 

Note that your should ALWAYS be checking the return value of the sscanf call to see if it worked.

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226