Thread: Does C execute in strict code order?

  1. #1
    Registered User
    Join Date
    Dec 2010
    Posts
    14

    Does C execute in strict code order?

    I have noticed in many of my programs that later code can affect the outcomes of earlier code. for example:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <math.h>
    
    #define A 1
    #define W 1
    #define K 1
    #define DT 0.01
    #define DX 0.01
    #define XMAX 1
    #define TMAX 1
    #define TENS 1
    #define DENS 1
    
    int main(int argc, char *argv[])
    {
      double u[10]={0};
      double v[10]={0};
      double a[10]={0};
      double local_equil;
      double deviation;
      double force;
      double t;
      int i;
      int valid_input;
      char *out_file;
      FILE *output;
      
      if(argc!=2){
        printf("not correct number of arguments\n");
        exit(EXIT_FAILURE);
      }
      
      valid_input = sscanf(argv[1],"%s",out_file);
      printf("valid_input = %d\n",valid_input);
      
      if(valid_input){
        printf("%s\n",out_file);
        printf("%d hooray\n",valid_input);
        output = fopen(out_file,"w");
      }
      
      if(!valid_input){
        printf("input fail\n");
        exit(EXIT_FAILURE);
      }
      
      for(t=0;t<100;t+=DT){
    
          u[0]=1;
    
        
        for(i=1;i<2;i++){
    
          local_equil = (u[i-1]+u[i+1])/2;
          deviation = local_equil - u[i];
          force = TENS * deviation;
          a[i] = force/(DENS*DX);
        }
    
        fprintf(output,"u[0] = %9f  ",u[0]);
        
        for(i=1;i<2;i++){
          u[i] = u[i] + v[i]*DT + 0.5 * a[i] * (DT*DT);
          v[i] = v[i] + (a[i] * DT);
          fprintf(output,"u[%d] = %9f  ",i,u[i]);
        }
        fprintf(output,"u[2] = 0\n");
    
      }
      
      return(0);
    }
    This code always says it has an invalid input, no matter what is put as the command line argument. However, if the entirety of the for loops are commented out, the input is read correctly.
    How is this possible, seeing as all the tests for valid input occur before those loops?

    I'm presuming that either i'm being very stupid or this is a by-product of optimisation. I use GCC on linux

    p.s. apologies for the disjointed code, it's been altered so much to try and get things to work that it's lost all elegance.

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    In the follow snippet:
    Code:
      char *out_file;
    
     valid_input = sscanf(argv[1],"%s",out_file);
    You have never assigned any memory to out file. Also this line does not make any sense. argv[1] is already a character string, if you want to out_file to be the same as argv[1] then, after you assign memory to out_file, you should use strcpy().

    Jim

  3. #3
    Registered User
    Join Date
    Dec 2010
    Posts
    14
    Thanks, problem solved, i replaced it with this

    Code:
      out_file = (char *) malloc(strlen(argv[1]));
      
      strcpy(out_file,argv[1]);
    I presume what I was seeing was undefined behaviour due to my mistake.

  4. #4
    Registered User
    Join Date
    Dec 2007
    Posts
    2,675
    You're still engaging in undefined behavior, because strlen doesn't include the space for the required null terminator character, so with that code you're writing into memory you don't own.

  5. #5
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Simple answer here....
    There is no need to test or copy argv[1] in your case. Just use this...

    Code:
    FILE *fp = NULL;
    
    // other code
    
    fp = fopen(argv[1],"w");
    if (! fp)
      { printf("Error invalid file name\n\n");
        exit(1); }
    The result is the same, if you provide a bad filename the program aborts. This because the fopen() call will fail and testing it's return value tells you if you want to continue the program or not...

    One of the big mistakes in C is overdoing things. I see this all the time, people creating unnecessary variables, writing loops that only execute once, and so on. People need to look at a program's logic with an eye to always finding the simplest way of doing things.

  6. #6
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by CommonTater View Post
    One of the big mistakes in C is overdoing things. I see this all the time, people creating unnecessary variables, writing loops that only execute once, and so on. People need to look at a program's logic with an eye to always finding the simplest way of doing things.
    I agree with you that there needs to be more work to simplify the logic of code, but the more common mistake I see in C is underdoing things, not overdoing things. Let's see .....

    Dynamically allocating one less item of memory than really needed, so later code falls off the end of an array.

    Dynamically allocating arrays with zero elements.

    Neglect to initialise a variable, and then later relying on that variable having a value of zero (or, sometimes, 42).

    Not breaking code into small and simple enough pieces, so the code structure is hard to understand.

    Using one-letter variable names like d and c as a briefer way of expressing some concept like dollars and cents.

    Not using enough variables, which makes code hard to understand, because the meaning of a variable name changes depending on what part of a function you are looking at.

    Making code terse, so one expression has five side-effects, which makes each line hard to understand and increases the chances of undefined behaviour.

    Putting a bunch of unrelated functions into one file named "misc.cc" or "utility.c", rather than putting those functions into a set of separate but more meaningfully named files.

    Relying on the user to adjust their behaviour because of insufficient error checking, or using uncontrolled methods of input. It is much simpler to type scanf("%s", buffer) or gets(buffer) than fgets(buffer, sizeof(buffer), stdin), after all.

    Assuming all compilers support 32 bit ints, and writing code that behaves strangely when rebuilt on other compilers when the user inputs a value greater than 70000 (or so).

    Using magic numbers rather than enumerated types. After all, we can rely on others remembering the distinct meanings we attach to 57 distinct integral values, and never getting things wrong.

    Using system() calls and wondering why things only seem to work right on the original development machine.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  7. #7
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Sadly, Grumpy, we both seem to be right...

    Ironically, after excessive pre-checks the OP never did check to see if the file actually opened or not.
    Last edited by CommonTater; 07-03-2011 at 05:11 PM.

  8. #8
    Registered User
    Join Date
    Dec 2010
    Posts
    14
    Thanks for the replies guys.

    RE no check as to whether the file opened, I had removed it just to be sure it wasn't causing the problem and it is now reinstated.

    The discussion on style in C is an interesting one. I am in no way an expert, but the person who taught me the basics was an ex-Boeing programmer and therefore believed that all code should be 100% safe in all possible conditions and furthermore that the ultimate goal was code that could be easily read, fully understood and debugged without having to run it.

    Am I working to an old fashioned standard? Should I in fact be condensing my code to more compound statements with less variables?

    here is the updated code, currently running smoothly:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    
    #define DAMP 0.001
    #define DT 0.0001
    #define DX 0.01
    #define NUMBER 101
    #define XMAX 1
    #define TMAX 15
    #define TENS 50
    #define DENS 1
    
    int main(int argc, char *argv[])
    {
      double u[NUMBER]={0};
      double v[NUMBER]={0};
      double a[NUMBER]={0};
      double local_equil;
      double deviation;
      double force;
      double t;
      int i;
      char *out_file;
      FILE *output;
      
      if(argc!=2){
        printf("not correct number of arguments\n");
        exit(EXIT_FAILURE);
      }
      
      out_file = (char *) malloc(strlen(argv[1])+1);
      
      strcpy(out_file,argv[1]);
      
      if(out_file != NULL){
        output = fopen(out_file,"w");
      }
      else{
        printf("output file name error");
        exit(EXIT_FAILURE);
      }
      
      if(output == (FILE *)NULL){
        printf("file opening failed\n");
        exit(EXIT_FAILURE);
      }
      
      for(i=0; i < NUMBER; i++){
        fprintf(output,"%lf  ",u[i]);
      }
      fprintf(output,"\n");
      
      for(t=0;t<TMAX;t+=DT){
        
        if(t <= 1)
          u[0] = sin(3.14159 * t);
        
        for(i=1; i<(NUMBER-1); i++){
          local_equil = (u[i-1]+u[i+1])/2;
          deviation = local_equil - u[i];
          force = TENS * deviation;
          a[i] = force/(DENS*DX);
        }
        fprintf(output,"%lf  ",u[0]);
        
        for(i=1; i<(NUMBER-1); i++){
          a[i] = a[i] - ((DAMP*v[i])/(DENS*DX));
          u[i] = u[i] + v[i]*DT + 0.5 * a[i] * (DT*DT);
          v[i] = v[i] + (a[i] * DT);
          fprintf(output,"%lf  ",u[i]);
        }
        fprintf(output,"%lf\n",u[NUMBER-1]);
      }
      
      return(0);
    }
    Last edited by Skeksis; 07-04-2011 at 03:49 AM.

  9. #9
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Skeksis
    The discussion on style in C is an interesting one. I am in no way an expert, but the person who taught me the basics was an ex-Boeing programmer and therefore believed that all code should be 100% safe in all possible conditions and furthermore that the ultimate goal was code that could be read, fully understood and debugged without necessarily having to run it.

    Am I working to an old fashioned standard? Should I in fact be condensing my code to more compound statements with less variables?
    The problem is that you are not working to this "old fashioned" standard by introducing unnecessary variables and statements. Simple code is more likely to be "100% safe in all possible conditions" and tends to be easier to "be read, fully understood and debugged without necessarily having to run it".

    EDIT:
    For example, one condition that you failed to consider is if malloc returns a null pointer. Why not just use argv[1]? If you want the name out_file for readability, write:
    Code:
    out_file = argv[1];
    If you really want to validate the filename then you should check that it conforms to some given format. Copying it does not validate it, and in fact your check for a null pointer at that point is futile because it should have been done before the strcpy (and a null pointer indicates a failure of memory allocation, not an invalid filename).
    Last edited by laserlight; 07-04-2011 at 04:02 AM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  10. #10
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Quote Originally Posted by Skeksis View Post
    I am in no way an expert, but the person who taught me the basics was an ex-Boeing programmer and therefore believed that all code should be 100% safe in all possible conditions and furthermore that the ultimate goal was code that could be easily read, fully understood and debugged without having to run it.
    I am flying this week, it better not be on one of these "fully understood without to having to run it" Boeing flight control systems.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  11. #11
    Registered User
    Join Date
    Dec 2010
    Posts
    14
    Quote Originally Posted by laserlight View Post
    The problem is that you are not working to this "old fashioned" standard by introducing unnecessary variables and statements. Simple code is more likely to be "100% safe in all possible conditions" and tends to be easier to "be read, fully understood and debugged without necessarily having to run it".

    EDIT:
    For example, one condition that you failed to consider is if malloc returns a null pointer. Why not just use argv[1]? If you want the name out_file for readability, write:
    Code:
    out_file = argv[1];
    If you really want to validate the filename then you should check that it conforms to some given format. Copying it does not validate it, and in fact your check for a null pointer at that point is futile because it should have been done before the strcpy (and a null pointer indicates a failure of memory allocation, not an invalid filename).
    Good points, I didn't notice a lot of that.
    I wasn't aware that the basic c operators such as = worked on strings. As a matter of fact i'm pretty certain they don't, or am i missing something.

  12. #12
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Skeksis
    I wasn't aware that the basic c operators such as = worked on strings. As a matter of fact i'm pretty certain they don't, or am i missing something.
    The assignment operator= does not work with arrays. However, it does work with pointers.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  13. #13
    Registered User
    Join Date
    Dec 2010
    Posts
    14
    ok, I understand.
    so now I have this as my input validation, which seems a lot cleaner and just generally better
    Code:
    if(argc!=2){
      printf("incorrect number of arguments\n");
      exit(EXIT_FAILURE);
    }
      
    z_matrix_out_fn = argv[1];
      
    z_matrix_out_fp = fopen(z_matrix_out_fn, "w");
      
    if(z_matrix_out_fp == (FILE *)NULL){
      printf("file opening failed\n");
      exit(EXIT_FAILURE);
    }

  14. #14
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Quote Originally Posted by claudiu View Post
    I am flying this week, it better not be on one of these "fully understood without to having to run it" Boeing flight control systems.
    If you are flying on a Boeing aircraft, then you probably are.

    Airworthiness regulators require a truckload of evidence that requirements for a system are sufficient to ensure a high confidence of safety, that all features in the design are derived from requirements (i.e. there cannot be a design feature that is not articulated in the requirements specification) and the code only implements features required of the design and no more.

    That type of process often means that there is a very deep understanding on the requirements a program is supposed to meet, and of the design in order to meet those requirements, before a single line of code is written. All of that understanding, and evidence of it, is documented. It also means that no line of code can be changed unless the need is reflected in documented design and requirements.

    Any testing by executing the resultant system has a specific purpose of providing additional evidence that requirements are complete, design reflects requirements, and code correctly implements design.

    Given a choice between an aircraft with a control system that has been through that type of development process, versus another aircraft with a control system hacked together by the typical commercial programmer who has trouble remembering to initialise an int variable, I know which I would choose to fly in.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  15. #15
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    Quote Originally Posted by grumpy View Post
    If you are flying on a Boeing aircraft, then you probably are.

    Airworthiness regulators require a truckload of evidence that requirements for a system are sufficient to ensure a high confidence of safety, that all features in the design are derived from requirements (i.e. there cannot be a design feature that is not articulated in the requirements specification) and the code only implements features required of the design and no more.

    That type of process often means that there is a very deep understanding on the requirements a program is supposed to meet, and of the design in order to meet those requirements, before a single line of code is written. All of that understanding, and evidence of it, is documented. It also means that no line of code can be changed unless the need is reflected in documented design and requirements.

    Any testing by executing the resultant system has a specific purpose of providing additional evidence that requirements are complete, design reflects requirements, and code correctly implements design.

    Given a choice between an aircraft with a control system that has been through that type of development process, versus another aircraft with a control system hacked together by the typical commercial programmer who has trouble remembering to initialise an int variable, I know which I would choose to fly in.
    It is quite funny you mention this because I used to work in requirements engineering and sometimes we had projects related to aircraft and naval operations. You would be really surprised to see how these requirements workshops go sometimes. A funny thing about the requirements is that they don't exist (which is why the term requirements elicitation is wrong) they are created. You don't go around with a butterfly net catching requirements, they are the production of (often conflicting) views coming from many stakeholders. Obviously, this means that the requirements process is just as error prone as the design process or the implementation process.

    In all honesty, there is obviously a great degree of diligence when dealing with safety critical systems like flight controllers but the development process for it is not as ideal as you make it sound. Business rules often interfere with the process, not to the point where they would be a safety hazard, but sufficient to influence the different stages. There are more non functional requirements related to cost, availability, maintainability and accessibility in most projects than there are functional ones.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Execute code from a text file?
    By george_1988 in forum C Programming
    Replies: 8
    Last Post: 09-15-2008, 03:32 PM
  2. How do classes execute code?
    By bargomer in forum C++ Programming
    Replies: 4
    Last Post: 05-09-2008, 03:10 PM
  3. How to know if my code is ANSI strict ?
    By jabka in forum C Programming
    Replies: 1
    Last Post: 10-19-2007, 07:32 AM
  4. execute a code in data segment...
    By ravindragjoshi in forum Windows Programming
    Replies: 1
    Last Post: 06-12-2007, 11:43 PM
  5. Need help on code to execute the program
    By hibmem10 in forum C++ Programming
    Replies: 6
    Last Post: 12-24-2005, 01:42 PM