Thread: Didn't use to need extern, now I do, don't know what changed? Can anyone help?

  1. #1
    Registered User
    Join Date
    Oct 2022
    Posts
    2

    Didn't use to need extern, now I do, don't know what changed? Can anyone help?

    I am a C newbie so I hope I am using the terms below correctly:

    I have Ubuntu 20.04; CMAKE 3.22.1 GCC 9.2.1 ARM-none-eabi-GCC;

    VScode is IDE.

    I wrote a C library for the RP2040 and a SPI IC a few months ago.
    The library required a buffer; so i declared a global array in the library's .h file.

    The library along with test code in main.c compiled OK at that time.

    I could call functions from the library in main.c without issue.

    However A few weeks ago I went to use the library again and got multiple declaration errors for the arrays declared earlier.

    I made no code changes.....but What used to work no longer worked.

    I fixed it by making the arrays in the library extern and then adding the same arrays declarations, without "extern" keyword, to main.c

    with those changes the whole thing compiles OK again

    but the library seemed more portable before.

    I am not sure what changed here?

    Here An example of code which compiles and runs in GDB online without error. This is a extremely simplified version of the library but the same general idea.

    ####################
    main.c
    ####################
    Code:
    #include <stdio.h>
    #include "ext1.h"
     
    int main()
    {
     mult_me(6);
     printf("value of c is: %d",c[0]);
    }

    ####################
    ext1.h
    ####################
    Code:
    //global array
    int c[1];
     
    int mult_me(int x);

    ####################
    ext1.c
    ####################

    Code:
    #include "ext1.h"
     
    int mult_me(int x)
    {
        c[0] = 7*x;
        
    }
    ####################

    If i run this same code on the Ubuntu/CMAKE/arm-none-eabi setup it throws multiple definition" issues for the variable c found in main.c

    I hope more experienced programmers can follow what I am saying.....

    I'd like to know why what used to compile OK suddenly stopped working. I assume it was an update of some kind but can't figure out what did it.

    Anyone who can shed some light on this, I'd be very grateful.

    Thank you

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,439
    It was 'fine' when all you had was a single .h file included in a single .c file.

    But the moment you have more than one .c file, you need to fix your approach (as you figured out).

    Generally, global data is a poor approach if there are other ways to solve the problem.

    Code:
    // main.c
    #include <stdio.h>
    #include "ext1.h"
    #define ASIZE 1
    int main()
    {
     int array[ASIZE];
     mult_me(array,ASIZE,6);
     printf("value of c is: %d",array[0]);
    }
    
    // ext1.h
    #ifndef EXT1_H_INCLUDED
    #define EXT1_H_INCLUDED
    int mult_me(int *array, int size, int x);
    #endif
    
    // ext1.c
    #include "ext1.h"
    int mult_me(int *array, int size, int x)
    {
        // 
        array[0] = 7*x;
    }
    By passing the array you want to change as a parameter (it's also good practice to always pass the size of any array), your function no longer cares what name you gave the array.

    Your header files should have include guards.
    See https://www.geeksforgeeks.org/include-guards-in-c/ for an explanation.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Oct 2022
    Posts
    2
    Thank you for your reply.

    Your header files should have include guards.
    See https://www.geeksforgeeks.org/include-guards-in-c/ for an explanation.
    For brevity I didn't include guards here, but they are in my library. I am aware that that is a best practice. Their absence does not impact my overarching question however.

    But the moment you have more than one .c file, you need to fix your approach (as you figured out).
    Of course, but respectfully, that's not my question.

    Why does code that used to compile, 2 or so months ago, and still compiles without error on online GDB's website, no longer compile?

    Does CMAKE, GCC (not sure which one but it was probably one of those) move the goal posts frequently in the world of C?

    I apologize if this is a newbie question, but I thought, perhaps incorrectly, that a big draw of C is that it almost never changes--what used to compile for processor X 10-20 years ago will probably still do so today, and something simple that compiles on the onlineGDB site (like the code below) will probably also compile on an Ubuntu linux host using CMAKE and GCC (it won't)

    Am I being naive?

    Perhaps there is some sort of filter in the CMAKE/GCC world I need to better understand, one that allows or disallows dicey programming practices?

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,439
    i dunno, what was your code 2 months ago?

    Try putting the verbose/debug on your cmake command line, to see what's it's doing.

    Just compiling directly using GCC works.
    Code:
    ==> ext1.c <==
    #include "ext1.h"
      
    int mult_me(int x)
    {
        c[0] = 7*x;    
    }
    
    ==> ext1.h <==
    //global array
    int c[1];
      
    int mult_me(int x);
    
    ==> main.c <==
    #include <stdio.h>
    #include "ext1.h"
      
    int main()
    {
     mult_me(6);
     printf("value of c is: %d",c[0]);
    }
    
    $ gcc -Wall -Wextra main.c ext1.c
    ext1.c: In function ‘mult_me’:
    ext1.c:7:1: warning: control reaches end of non-void function [-Wreturn-type]
        7 | }
          | ^
    
    $ ./a.out 
    value of c is: 42$
    Sure, there's a warning, but nothing catastrophic like multiply declared symbols.

    > This is a extremely simplified version of the library but the same general idea.
    You need to post an extremely simplified version that actually represents the problem, along with your actual error messages and whatever cmake file you used.

    Otherwise, it's just hand-waving and "works for me".
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    1,682
    Quote Originally Posted by audiodiwhy View Post
    Thank you for your reply.



    For brevity I didn't include guards here, but they are in my library. I am aware that that is a best practice. Their absence does not impact my overarching question however.



    Of course, but respectfully, that's not my question.

    Why does code that used to compile, 2 or so months ago, and still compiles without error on online GDB's website, no longer compile?

    Does CMAKE, GCC (not sure which one but it was probably one of those) move the goal posts frequently in the world of C?

    I apologize if this is a newbie question, but I thought, perhaps incorrectly, that a big draw of C is that it almost never changes--what used to compile for processor X 10-20 years ago will probably still do so today, and something simple that compiles on the onlineGDB site (like the code below) will probably also compile on an Ubuntu linux host using CMAKE and GCC (it won't)

    Am I being naive?

    Perhaps there is some sort of filter in the CMAKE/GCC world I need to better understand, one that allows or disallows dicey programming practices?
    When multiple files include the same header they also define the same things, when it's just the name it's fine as the linker is then responsible for collecting the name definitions and linking them to the contents defined in the files that included those headers. When you define the contents in headers, barring certain exceptions designed specifically for headers like inline functions, you're also defining those same contents multiple times, as a result the files can and will see different addresses for the same name which puts everything out of sync, decidedly the opposite of what you intended, that is the reason defining the contents of a name is generally forbidden in headers. The compiler (gcc in this case) won't be able to catch that area because for all it knows, those headers were specifically designed to be included by just one file (bad practice but still legitimate), the linker on the other hand can catch those areas with the information kept within the object files created by the compiler, object files that were created from the source files that included the headers that defined the contents of the symbol you meant to have synced between files, so in reality, assuming you used multiple files when it worked then the version of the linker &/or compiler you used at the time was not properly made, in other words it was a bug that caused your original code to work, not a standard feature.

  6. #6
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    904
    int c[1]; is a tentative definition (per that link, "Tentative definitions were invented to standardize various pre-C89 approaches to forward declaring identifiers with internal linkage").

    But then you have Porting to GCC 10 which essentially says this feature is disabled by default in GCC 10 (which may or may not be the version you're using) and that you need to use extern on those declarations (and also have one definition in only one compilation unit). You could try using the -fcommon compiler flag to get the tentative definition feature back.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 1
    Last Post: 04-13-2020, 09:48 PM
  2. Replies: 9
    Last Post: 05-07-2017, 12:29 PM
  3. Replies: 21
    Last Post: 06-24-2011, 02:57 AM
  4. Replies: 17
    Last Post: 12-15-2006, 11:02 AM
  5. Help! (Didn't know were to put this...)
    By minime6696 in forum Windows Programming
    Replies: 2
    Last Post: 02-23-2002, 07:43 PM

Tags for this Thread