Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #162690 > unrolled thread

Usage of static in array declaration

Started byThiago Adams <thiago.adams@gmail.com>
First post2021-09-10 07:38 -0700
Last post2021-09-10 11:26 -0700
Articles 7 — 4 participants

Back to article view | Back to comp.lang.c


Contents

  Usage of static in array declaration Thiago Adams <thiago.adams@gmail.com> - 2021-09-10 07:38 -0700
    Re: Usage of static in array declaration Andrey Tarasevich <andreytarasevich@hotmail.com> - 2021-09-10 07:58 -0700
      Re: Usage of static in array declaration Thiago Adams <thiago.adams@gmail.com> - 2021-09-10 09:17 -0700
        Re: Usage of static in array declaration Thiago Adams <thiago.adams@gmail.com> - 2021-09-10 09:37 -0700
        Re: Usage of static in array declaration "james...@alumni.caltech.edu" <jameskuyper@alumni.caltech.edu> - 2021-09-10 09:46 -0700
    Re: Usage of static in array declaration "james...@alumni.caltech.edu" <jameskuyper@alumni.caltech.edu> - 2021-09-10 08:32 -0700
      Re: Usage of static in array declaration Keith Thompson <Keith.S.Thompson+u@gmail.com> - 2021-09-10 11:26 -0700

#162690 — Usage of static in array declaration

FromThiago Adams <thiago.adams@gmail.com>
Date2021-09-10 07:38 -0700
SubjectUsage of static in array declaration
Message-ID<51e13b69-37ba-46e1-95eb-f30dd76590efn@googlegroups.com>
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);
}

[toc] | [next] | [standalone]


#162692

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2021-09-10 07:58 -0700
Message-ID<shfrrm$4ej$1@dont-email.me>
In reply to#162690
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

[toc] | [prev] | [next] | [standalone]


#162701

FromThiago Adams <thiago.adams@gmail.com>
Date2021-09-10 09:17 -0700
Message-ID<c37bccae-2da5-433b-96cd-3f593d68117en@googlegroups.com>
In reply to#162692
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.

[toc] | [prev] | [next] | [standalone]


#162704

FromThiago Adams <thiago.adams@gmail.com>
Date2021-09-10 09:37 -0700
Message-ID<38a7f13f-e511-4332-b31c-613cd183c1edn@googlegroups.com>
In reply to#162701
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.

[toc] | [prev] | [next] | [standalone]


#162707

From"james...@alumni.caltech.edu" <jameskuyper@alumni.caltech.edu>
Date2021-09-10 09:46 -0700
Message-ID<a7b4d74a-75dd-4a37-8570-f388f3eeae35n@googlegroups.com>
In reply to#162701
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.

[toc] | [prev] | [next] | [standalone]


#162695

From"james...@alumni.caltech.edu" <jameskuyper@alumni.caltech.edu>
Date2021-09-10 08:32 -0700
Message-ID<b21d04d8-b701-4f38-8398-abac128dae38n@googlegroups.com>
In reply to#162690
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.

[toc] | [prev] | [next] | [standalone]


#162715

FromKeith Thompson <Keith.S.Thompson+u@gmail.com>
Date2021-09-10 11:26 -0700
Message-ID<87czpgffsw.fsf@nosuchdomain.example.com>
In reply to#162695
"james...@alumni.caltech.edu" <jameskuyper@alumni.caltech.edu> writes:
> 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.

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.Thompson+u@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web