Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Usage of static in array declaration

41 views
Skip to first unread message

Thiago Adams

unread,
Sep 10, 2021, 10:39:06 AM9/10/21
to

It is not clear to me why this static was created and
what is the behavior.

For instance,
void F(char name[static 10]){
}

Can I pass a pointer to char*?

I will check some compilers to see what is the actual behavior


gcc 11.2

void f(char name[static 10]){}

int main()
{
char *p = "abc";
f(p);
}
gives me the same warning with our without static.

warning: 'f' accessing 10 bytes in a region of size 4 [-Wstringop-overflow=]

no warning here

void f(char name[static 10]){}

extern char* s1;
int main()
{
f(s1);
}

Andrey Tarasevich

unread,
Sep 10, 2021, 10:59:15 AM9/10/21
to
On 9/10/2021 7:38 AM, Thiago Adams wrote:
>
> It is not clear to me why this static was created and
> what is the behavior.

I think it is pretty clearly explained in the standard and elsewhere: to
help the compiler to optimize the code.

> For instance,
> void F(char name[static 10]){
> }
>
> Can I pass a pointer to char*?

Of course. The parameter type is still `char *`. What prompted the question?

> I will check some compilers to see what is the actual behavior
>
> gcc 11.2
>
> void f(char name[static 10]){}
>
> int main()
> {
> char *p = "abc";
> f(p);
> }
> gives me the same warning with our without static.
>
> warning: 'f' accessing 10 bytes in a region of size 4 [-Wstringop-overflow=]
>
> no warning here
>
> void f(char name[static 10]){}
>
> extern char* s1;
> int main()
> {
> f(s1);
> }
>

For obvious reasons, the compiler was "smart enough" to issue the
warning in the first case. In the second case it is simply impossible.

This feature will produce a tangible difference in code generation. For
example, such parameter cannot be null. For example, Clang will
aggressively discard branches that would've been taken for a null parameter

https://godbolt.org/z/Y5KKzPbv8

--
Best regards,
Andrey Tarasevich

james...@alumni.caltech.edu

unread,
Sep 10, 2021, 11:32:49 AM9/10/21
to
On Friday, September 10, 2021 at 10:39:06 AM UTC-4, Thiago Adams wrote:
> It is not clear to me why this static was created and
> what is the behavior.
>
> For instance,
> void F(char name[static 10]){
> }
>
> Can I pass a pointer to char*?

Sure. If you ignore the "static", that declaration is equivalent to "char *name".
The static keyword imposes restrictions on the values that you can safely
pass to the function, but has no effect on the types that you can pass.

"If the keyword static also appears within the [ and ] of the array type
derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at
least as many elements as specified by the size expression." (6.7.6.3p7).

Note that this is a "shall" that does not occur in a constraints section. As
such, code which violates this "shall" has undefined behavior (4p2). No
diagnostic is required. This is because it could be arbitrarily difficult to
determine at compile time whether or not that is the case. However, good
quality implementation should generate a diagnostic whenever they can
determine that it is violated, and that optional diagnostic is, in my opinion,
the main justification for this feature. I've heard it claimed that it enables
optimizations, but I've never heard an explanation of what those
optimizations might be.

Thiago Adams

unread,
Sep 10, 2021, 12:17:20 PM9/10/21
to
On Friday, September 10, 2021 at 11:59:15 AM UTC-3, Andrey Tarasevich wrote:
> On 9/10/2021 7:38 AM, Thiago Adams wrote:
> >
> > It is not clear to me why this static was created and
> > what is the behavior.
> I think it is pretty clearly explained in the standard and elsewhere: to
> help the compiler to optimize the code.
> > For instance,
> > void F(char name[static 10]){
> > }
> >
> > Can I pass a pointer to char*?
> Of course. The parameter type is still `char *`. What prompted the question?

I thought the compiler would ensure the caller is a fixed length array
and emit warnings if not.

> > I will check some compilers to see what is the actual behavior
> >
> > gcc 11.2
> >
> > void f(char name[static 10]){}
> >
> > int main()
> > {
> > char *p = "abc";
> > f(p);
> > }
> > gives me the same warning with our without static.
> >
> > warning: 'f' accessing 10 bytes in a region of size 4 [-Wstringop-overflow=]
> >
> > no warning here
> >
> > void f(char name[static 10]){}
> >
> > extern char* s1;
> > int main()
> > {
> > f(s1);
> > }
> >
> For obvious reasons, the compiler was "smart enough" to issue the
> warning in the first case. In the second case it is simply impossible.
>
> This feature will produce a tangible difference in code generation. For
> example, such parameter cannot be null. For example, Clang will
> aggressively discard branches that would've been taken for a null parameter
>
> https://godbolt.org/z/Y5KKzPbv8

Interesting but the code doesn't look much realistic.

if we take a real function like:
int strcmp ( const char * str1, const char * str2 );

it could be:

int strcmp ( const char str1[static 1], const char str2[static 1]);

but the generated code is the same because strcmp is already
considering that str1 and str2 are not null.

It would be nice to have static analysis checks but I believe compilers like
gcc already do it independently of the [static ].

So the feature is still confuse.
It is a better contract but how to use this in a practical way?

Here there are interesting comments referring to MISRA.
https://rules.sonarsource.com/c/RSPEC-1831

" Therefore, in practice the use of static on an array parameter’s size merely
lends a false sense of security, and static should not be used in this context."

I think if the behavior were only accept fixed length arrays that we can check
100% it would be a more useful feature. In the worst it is just a matter of create
two versions of the same function.

itoa could be a sample of that.
Let's say we have a itoa10 for instance for radix 10 it could ask a fixed length buffer
for input.

Thiago Adams

unread,
Sep 10, 2021, 12:37:47 PM9/10/21
to
On Friday, September 10, 2021 at 1:17:20 PM UTC-3, Thiago Adams wrote:
...
> I think if the behavior were only accept fixed length arrays that we can check
> 100% it would be a more useful feature. In the worst it is just a matter of create
> two versions of the same function.

This is an emulation of that idea

#define static_itoa(value, str, base)\
_Static_assert(sizeof(str) >= 33, "we need at least 33 chars");\
itoa(value, str, base)

char buffer[33];
static_itoa(12345, buffer, 10);

The problem it does not work when the sized required is the same of pointer size.

james...@alumni.caltech.edu

unread,
Sep 10, 2021, 12:46:52 PM9/10/21
to
On Friday, September 10, 2021 at 12:17:20 PM UTC-4, Thiago Adams wrote:
> On Friday, September 10, 2021 at 11:59:15 AM UTC-3, Andrey Tarasevich wrote:
> > On 9/10/2021 7:38 AM, Thiago Adams wrote:
> > >
> > > It is not clear to me why this static was created and
> > > what is the behavior.
> > I think it is pretty clearly explained in the standard and elsewhere: to
> > help the compiler to optimize the code.
> > > For instance,
> > > void F(char name[static 10]){
> > > }
> > >
> > > Can I pass a pointer to char*?
> > Of course. The parameter type is still `char *`. What prompted the question?
> I thought the compiler would ensure the caller is a fixed length array
> and emit warnings if not.

It can't do that in general at compile time. The pointer might point at an element
of an array defined in some other translation unit. It might point into dynamically
allocated memory, with the amount of memory that was allocated being
unknown at compile time. That's why a diagnostic is not mandatory - but good
quality implementations will warn you when it is feasible to identify violations of
this requirement.

Keith Thompson

unread,
Sep 10, 2021, 2:26:18 PM9/10/21
to
Right. All the "static" keyword does is cause some calls to have
undefined behavior that would not have UB if the "static" were removed.

A naive compiler can treat `char name[static 10]` as exactly equivalent
to `char name[]` or `char *name`.

A sufficiently clever compiler can use the `static 10` to warn about
possible undefined behavior (though there will always be cases where it
can't do so reliably) and generate code in a way that assumes the
behavior is well defined.

For example:

void F(char name[static 10]) {
if (name != NULL) {
/* ... */
}
}

An optimizing compiler could remove the NULL check.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */
0 new messages