0

I would like to know what the legal way of defining a constant struct that has a pointer as one of it's elements. Looking at this post (Initializer element is not constant in C) I can see the following:

6.6 Constant expressions

(7) More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:

— an arithmetic constant expression,

— a null pointer constant,

— an address constant, or

— an address constant for an object type plus or minus an integer constant expression.

(8) An arithmetic constant expression shall have an arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, and sizeof expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to a sizeof operator whose result is an integer constant.

My question is if the following is well defined according to the (intersection of the C89 and C99) standard. The contents of test.h:

#ifndef TEST_H
#define TEST_H

typedef struct _poly {
    unsigned long int degree;
    signed long int *coeffs;
} poly;

extern const poly my_poly;
extern void print_poly(poly p);

#endif

Contents of test.c:

#include <stdio.h>
#include "test.h"

static signed long int coeffs[3] = {1L, 2L, 3L};
const poly my_poly = {2UL, coeffs};

void print_poly(poly p)
{
    /* Irrelevant mumbo-jumbo goes here. */
}

Contents of main.c:

#include "test.h"

int main(void)
{
    print_poly(my_poly);
    return 0;
}

Compiling on Debian 11 with gcc (and -Wall -Wextra -Wpedantic -std=c89 enabled), clang (with -Weverything -std=c89 enabled), tcc (with -Wall -std=c89 enabled), and pcc (with -std=c89 enabled) produces no warnings and no errors and runs as expected. Is the code:

static signed long int coeffs[3] = {1L, 2L, 3L};
const poly my_poly = {2UL, coeffs};

the correct way to initialize a constant struct that has a pointer as one of its member so that it is a compiler-time constant? It seems to follow the rule that it be an address constant, but I'm not sure.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 1
    I guess you self-answered your own question. It's legal – tstanisl Jan 30 '22 at 22:05
  • Yes it is legal, `coeffs` is an address constant. – n. m. could be an AI Jan 30 '22 at 22:22
  • You have quoted text that does not appear in C 1989/1990 (C 1990 6.4 has much the same text but with some different wording) but are compiling with `-std=c89`. Which specific version of the C standard do you want an answer for? Why are you compiling with a 30-year-old standard? – Eric Postpischil Jan 31 '22 at 00:21
  • In case of C99 you might want to do `const poly my_poly = { .degree = 2UL, .coeffs = (signed long){1L, 2L, 3L} };` for readability. – Lundin Jan 31 '22 at 09:47
  • @EricPostpischil I'm trying to be as portable as possible, so aiming for the intersection of C89, C99, C11, and C18 (which isn't too hard, mostly). I write mostly for unix-like platforms (GNU/Linux, macOS), and learned the hard way my code wouldn't run on Windows since MSVC does not support C99 features (complex.h, though they now have the C11 macro __STDC_NO_COMPLEX__ defined). I try testing my code on many compilers (GCC, clang, TCC, PCC, MSVC), but that's not a guarantee I wrote the code to the standard. – Ryan Maguire Jan 31 '22 at 14:08

1 Answers1

2

As the question notes, an initializer may be an address constant.

C 2018 6.6 9 says “An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type…”

In const poly my_poly = {2UL, coeffs};, coeffs is an array of static storage duration, and it is implicitly converted to a pointer to its first element (per C 2018 6.3.2.1 3). Therefore, the result of the conversion, effectively &coeffs[0], is an address constant and may be used as an initializer.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312