Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #107805 > unrolled thread
| Started by | MehdiAmini <atorrses@gmail.com> |
|---|---|
| First post | 2017-04-07 12:20 +0430 |
| Last post | 2017-04-08 20:34 +0430 |
| Articles | 20 on this page of 55 — 9 participants |
Back to article view | Back to comp.lang.c
How to fix: [-Wformat-nonliteral] MehdiAmini <atorrses@gmail.com> - 2017-04-07 12:20 +0430
Re: How to fix: [-Wformat-nonliteral] Barry Schwarz <schwarzb@dqel.com> - 2017-04-07 01:28 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-07 11:58 +0200
Re: How to fix: [-Wformat-nonliteral] Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-04-07 11:43 +0100
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-07 13:35 +0200
Re: How to fix: [-Wformat-nonliteral] Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-04-07 13:50 +0100
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-07 15:54 +0200
Re: How to fix: [-Wformat-nonliteral] Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-04-07 19:35 +0100
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-08 13:41 +0200
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-10 13:52 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-11 00:18 +0200
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-10 15:43 -0700
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-10 16:10 -0700
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-10 16:26 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-11 11:33 +0200
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-11 08:37 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-13 00:20 +0200
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-12 15:55 -0700
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-12 18:17 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-14 12:27 +0200
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-14 11:57 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-17 18:08 +0200
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-17 09:38 -0700
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-12 18:13 -0700
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-13 07:37 -0700
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-13 08:43 -0700
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-13 11:24 -0700
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-13 11:49 -0700
Re: How to fix: [-Wformat-nonliteral] Robert Wessel <robertwessel2@yahoo.com> - 2017-04-13 14:43 -0500
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-13 13:26 -0700
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-13 13:32 -0700
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-13 14:27 -0700
Re: How to fix: [-Wformat-nonliteral] Robert Wessel <robertwessel2@yahoo.com> - 2017-04-13 17:39 -0500
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-13 16:16 -0700
Re: How to fix: [-Wformat-nonliteral] Tim Rentsch <txr@alumni.caltech.edu> - 2017-04-16 17:53 -0700
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-13 13:19 -0700
Re: How to fix: [-Wformat-nonliteral] Tim Rentsch <txr@alumni.caltech.edu> - 2017-04-16 17:49 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-17 18:14 +0200
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-17 11:54 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-17 22:41 +0200
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-17 15:02 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-14 12:40 +0200
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-14 12:10 -0700
Re: How to fix: [-Wformat-nonliteral] Keith Thompson <kst-u@mib.org> - 2017-04-14 13:22 -0700
Re: How to fix: [-Wformat-nonliteral] supercat@casperkitty.com - 2017-04-14 17:14 -0700
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-13 00:20 +0200
Re: How to fix: [-Wformat-nonliteral] MehdiAmini <atorrses@gmail.com> - 2017-04-07 20:29 +0430
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-07 18:42 +0200
Re: How to fix: [-Wformat-nonliteral] MehdiAmini <atorrses@gmail.com> - 2017-04-08 08:04 +0430
Re: How to fix: [-Wformat-nonliteral] Richard Heathfield <rjh@cpax.org.uk> - 2017-04-08 07:51 +0100
Re: How to fix: [-Wformat-nonliteral] MehdiAmini <atorrses@gmail.com> - 2017-04-08 12:11 +0430
Re: How to fix: [-Wformat-nonliteral] Richard Heathfield <rjh@cpax.org.uk> - 2017-04-08 11:38 +0100
Re: How to fix: [-Wformat-nonliteral] Ben Bacarisse <ben.usenet@bsb.me.uk> - 2017-04-08 18:52 +0100
Re: How to fix: [-Wformat-nonliteral] David Brown <david.brown@hesbynett.no> - 2017-04-08 13:48 +0200
Re: How to fix: [-Wformat-nonliteral] MehdiAmini <atorrses@gmail.com> - 2017-04-08 20:34 +0430
Page 1 of 3 [1] 2 3 Next page →
| From | MehdiAmini <atorrses@gmail.com> |
|---|---|
| Date | 2017-04-07 12:20 +0430 |
| Subject | How to fix: [-Wformat-nonliteral] |
| Message-ID | <oc7g6c$lpq$1@dont-email.me> |
Hi,
Compiling this function using Clang:
noreturn void my_err(const char * const s ,...)
{
va_list args;
va_start(args, s);
if( (s==NULL) || ( *s=='\0') )
{
fprintf(stderr,"Please provide a proper message for my_err
function.\n");
exit(EXIT_FAILURE);
}
vfprintf(stderr,s,args);
fprintf(stderr,"\n");
va_end(args);
exit(EXIT_FAILURE);
}
I get:
warning: format string is not a string literal [-Wformat-nonliteral]
vfprintf(stderr,s,args);
^
How to fix the warning?
--
www.my-c-codes.com/
Farewell.
[toc] | [next] | [standalone]
| From | Barry Schwarz <schwarzb@dqel.com> |
|---|---|
| Date | 2017-04-07 01:28 -0700 |
| Message-ID | <3djeec5to8uf3vuui5vb8evcqtjb51ilk1@4ax.com> |
| In reply to | #107805 |
On Fri, 7 Apr 2017 12:20:25 +0430, MehdiAmini <atorrses@gmail.com>
wrote:
>Hi,
>
>Compiling this function using Clang:
>
>noreturn void my_err(const char * const s ,...)
>{
>
> va_list args;
> va_start(args, s);
>
> if( (s==NULL) || ( *s=='\0') )
> {
> fprintf(stderr,"Please provide a proper message for my_err
>function.\n");
> exit(EXIT_FAILURE);
> }
>
>
> vfprintf(stderr,s,args);
>
> fprintf(stderr,"\n");
>
> va_end(args);
>
> exit(EXIT_FAILURE);
>}
>
>I get:
>warning: format string is not a string literal [-Wformat-nonliteral]
> vfprintf(stderr,s,args);
> ^
>
>How to fix the warning?
Specify -Wno-format-nonliteral
--
Remove del for email
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-07 11:58 +0200 |
| Message-ID | <oc7nm7$d6i$1@dont-email.me> |
| In reply to | #107805 |
On 07/04/17 09:50, MehdiAmini wrote:
> Hi,
>
> Compiling this function using Clang:
>
> noreturn void my_err(const char * const s ,...)
> {
>
> va_list args;
> va_start(args, s);
>
> if( (s==NULL) || ( *s=='\0') )
> {
> fprintf(stderr,"Please provide a proper message for my_err
> function.\n");
> exit(EXIT_FAILURE);
> }
>
>
> vfprintf(stderr,s,args);
>
> fprintf(stderr,"\n");
>
> va_end(args);
>
> exit(EXIT_FAILURE);
> }
>
> I get:
> warning: format string is not a string literal [-Wformat-nonliteral]
> vfprintf(stderr,s,args);
> ^
>
> How to fix the warning?
>
Another poster showed you how to disable the warning. But you might
also like to consider /why/ clang has this warning - and if you would
rather change the logic of your function instead of disabling the warning.
The format string of the printf functions is like a little specialised
interpreted language. Get the syntax wrong, or get a mismatch in the
arguments, and you can break all sorts of things in all sorts of ways.
This can be due to simple bugs, or intentional security breaches. And
in a function like this one, which will typically rarely be called
during testing, it is easy to get things wrong.
So it would be /much/ safer to have the function as:
noreturn void my_err_s(const char * const s)
{
if ((s == NULL) || (*s == '\0')) {
fprintf(stderr, "Please provide a proper message for "
"my_err function.\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "%s\n", s);
exit(EXIT_FAILURE);
}
#define my_errPrintfBuffLen 256
#define my_err(f, x...) \
do { \
char buff[my_errPrintfBuffLen]; \
snprintf(buff, sizeof(buff) - 1, f, ## x); \
buff[my_errPrintfBuffLen - 1] = 0; \
my_err_s(buff); \
} while (0)
This makes any formatting be handled locally in the function that calls
my_err, where the compiler can see the format string as a literal and
check the format against the actual parameters used. (There is, IIRC, a
slight gcc'ism in the token pasting here to get the commas right even if
my_err is called with no extra parameters. But I expect clang supports
the same feature.)
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-04-07 11:43 +0100 |
| Message-ID | <87shlkv6om.fsf@bsb.me.uk> |
| In reply to | #107807 |
David Brown <david.brown@hesbynett.no> writes:
> On 07/04/17 09:50, MehdiAmini wrote:
<snip>
>> vfprintf(stderr,s,args);
<snip>
>> I get:
>> warning: format string is not a string literal [-Wformat-nonliteral]
>> vfprintf(stderr,s,args);
<snip>
> Another poster showed you how to disable the warning.
<snip>
> So it would be /much/ safer to have the function as:
>
> noreturn void my_err_s(const char * const s)
> {
> if ((s == NULL) || (*s == '\0')) {
> fprintf(stderr, "Please provide a proper message for "
> "my_err function.\n");
> exit(EXIT_FAILURE);
> }
> fprintf(stderr, "%s\n", s);
> exit(EXIT_FAILURE);
> }
>
> #define my_errPrintfBuffLen 256
> #define my_err(f, x...) \
> do { \
> char buff[my_errPrintfBuffLen]; \
> snprintf(buff, sizeof(buff) - 1, f, ## x); \
> buff[my_errPrintfBuffLen - 1] = 0; \
> my_err_s(buff); \
> } while (0)
>
> This makes any formatting be handled locally in the function that calls
> my_err, where the compiler can see the format string as a literal and
> check the format against the actual parameters used. (There is, IIRC, a
> slight gcc'ism in the token pasting here to get the commas right even if
> my_err is called with no extra parameters. But I expect clang supports
> the same feature.)
There are two GNUisms -- one is the comma pasting trick to handle zero
variable arguments, and the other is naming the variable arguments as x.
In fact, you don't need either as there is always one required
argument. This is standard C99 and up:
#define my_err(...) \
do { \
char buff[my_errPrintfBuffLen]; \
snprintf(buff, sizeof(buff) - 1, __VA_ARGS__); \
buff[my_errPrintfBuffLen - 1] = 0; \
my_err_s(buff); \
} while (0)
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-07 13:35 +0200 |
| Message-ID | <oc7tcd$vi2$1@dont-email.me> |
| In reply to | #107808 |
On 07/04/17 12:43, Ben Bacarisse wrote:
> David Brown <david.brown@hesbynett.no> writes:
>
>> On 07/04/17 09:50, MehdiAmini wrote:
> <snip>
>>> vfprintf(stderr,s,args);
> <snip>
>>> I get:
>>> warning: format string is not a string literal [-Wformat-nonliteral]
>>> vfprintf(stderr,s,args);
> <snip>
>
>> Another poster showed you how to disable the warning.
> <snip>
>> So it would be /much/ safer to have the function as:
>>
>> noreturn void my_err_s(const char * const s)
>> {
>> if ((s == NULL) || (*s == '\0')) {
>> fprintf(stderr, "Please provide a proper message for "
>> "my_err function.\n");
>> exit(EXIT_FAILURE);
>> }
>> fprintf(stderr, "%s\n", s);
>> exit(EXIT_FAILURE);
>> }
>>
>> #define my_errPrintfBuffLen 256
>> #define my_err(f, x...) \
>> do { \
>> char buff[my_errPrintfBuffLen]; \
>> snprintf(buff, sizeof(buff) - 1, f, ## x); \
>> buff[my_errPrintfBuffLen - 1] = 0; \
>> my_err_s(buff); \
>> } while (0)
>>
>> This makes any formatting be handled locally in the function that calls
>> my_err, where the compiler can see the format string as a literal and
>> check the format against the actual parameters used. (There is, IIRC, a
>> slight gcc'ism in the token pasting here to get the commas right even if
>> my_err is called with no extra parameters. But I expect clang supports
>> the same feature.)
>
> There are two GNUisms -- one is the comma pasting trick to handle zero
> variable arguments, and the other is naming the variable arguments as x.
>
> In fact, you don't need either as there is always one required
> argument. This is standard C99 and up:
>
> #define my_err(...) \
> do { \
> char buff[my_errPrintfBuffLen]; \
> snprintf(buff, sizeof(buff) - 1, __VA_ARGS__); \
> buff[my_errPrintfBuffLen - 1] = 0; \
> my_err_s(buff); \
> } while (0)
>
Good point. The macro was a modified copy of something from my own
code, which in this case already has plenty of GNUisms - a couple more
won't affect portability! And while the OP here is using clang, which I
believe supports these extensions, removing them makes the code more
portable for other people.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-04-07 13:50 +0100 |
| Message-ID | <87mvbsv0tq.fsf@bsb.me.uk> |
| In reply to | #107807 |
David Brown <david.brown@hesbynett.no> writes:
<snip>
> #define my_errPrintfBuffLen 256
> #define my_err(f, x...) \
> do { \
> char buff[my_errPrintfBuffLen]; \
> snprintf(buff, sizeof(buff) - 1, f, ## x); \
> buff[my_errPrintfBuffLen - 1] = 0; \
I was also going to mention this. It seems a bit belt and braces. Is
there any problem with
snprintf(buff, sizeof(buff), f, ## x); \
and no extra "buff[my_errPrintfBuffLen - 1] = 0;"?
Incidentally, mixing the use of my_errPrintfBuffLen and sizeof buff is
less the ideal. I'd either use the defined constant only in the
declaration and sizeof buff - 1 in both other places, or, if I felt the
need to avoid confusing people with sizeof, I'd use the defined constant
everywhere.
> my_err_s(buff); \
> } while (0)
I might even just write
#define my_err(...) \
do { \
char buff[256]; \
snprintf(buff, sizeof(buff), __VA_ARGS__); \
my_err_s(buff); \
} while (0)
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-07 15:54 +0200 |
| Message-ID | <oc85gk$s8g$1@dont-email.me> |
| In reply to | #107813 |
On 07/04/17 14:50, Ben Bacarisse wrote:
> David Brown <david.brown@hesbynett.no> writes:
> <snip>
>> #define my_errPrintfBuffLen 256
>> #define my_err(f, x...) \
>> do { \
>> char buff[my_errPrintfBuffLen]; \
>> snprintf(buff, sizeof(buff) - 1, f, ## x); \
>> buff[my_errPrintfBuffLen - 1] = 0; \
>
> I was also going to mention this. It seems a bit belt and braces. Is
> there any problem with
>
> snprintf(buff, sizeof(buff), f, ## x); \
>
> and no extra "buff[my_errPrintfBuffLen - 1] = 0;"?
Looks like you are right - snprintf guarantees to put a null character
at the end. The history of this macro in my code stretches back to use
with older compilers from long ago, where I was less confident in the
accuracy of the C library implementation. I can't remember if I
actually found a problem with a missing termination, or merely thought
it was a possibility.
>
> Incidentally, mixing the use of my_errPrintfBuffLen and sizeof buff is
> less the ideal. I'd either use the defined constant only in the
> declaration and sizeof buff - 1 in both other places, or, if I felt the
> need to avoid confusing people with sizeof, I'd use the defined constant
> everywhere.
Either method would work - I view the two expressions as synonymous and
interchangeable. It might be a little different if the uses were
separated over a longer distance in code rather than being adjacent lines.
And I know that some people think it is nice to write "sizeof(buff)",
others prefer "sizeof buff". I'd rather not argue too much about style
here.
>
>> my_err_s(buff); \
>> } while (0)
>
> I might even just write
>
> #define my_err(...) \
> do { \
> char buff[256]; \
> snprintf(buff, sizeof(buff), __VA_ARGS__); \
> my_err_s(buff); \
> } while (0)
>
That would be fine.
[toc] | [prev] | [next] | [standalone]
| From | Ben Bacarisse <ben.usenet@bsb.me.uk> |
|---|---|
| Date | 2017-04-07 19:35 +0100 |
| Message-ID | <87h920ukud.fsf@bsb.me.uk> |
| In reply to | #107816 |
David Brown <david.brown@hesbynett.no> writes:
> On 07/04/17 14:50, Ben Bacarisse wrote:
>> David Brown <david.brown@hesbynett.no> writes:
>> <snip>
>>> #define my_errPrintfBuffLen 256
>>> #define my_err(f, x...) \
>>> do { \
>>> char buff[my_errPrintfBuffLen]; \
>>> snprintf(buff, sizeof(buff) - 1, f, ## x); \
>>> buff[my_errPrintfBuffLen - 1] = 0; \
>>
>> I was also going to mention this. It seems a bit belt and braces. Is
>> there any problem with
>>
>> snprintf(buff, sizeof(buff), f, ## x); \
>>
>> and no extra "buff[my_errPrintfBuffLen - 1] = 0;"?
>
> Looks like you are right - snprintf guarantees to put a null character
> at the end. The history of this macro in my code stretches back to use
> with older compilers from long ago, where I was less confident in the
> accuracy of the C library implementation. I can't remember if I
> actually found a problem with a missing termination, or merely thought
> it was a possibility.
Yes, that was why I asked. The sn* family predate the C99 standard but
I don't recall any that would not generate a string (unless the size is
zero of course). But then my memory is not what it was, so I asked if
you had had reason to be suspicious.
>> Incidentally, mixing the use of my_errPrintfBuffLen and sizeof buff is
>> less the ideal. I'd either use the defined constant only in the
>> declaration and sizeof buff - 1 in both other places, or, if I felt the
>> need to avoid confusing people with sizeof, I'd use the defined constant
>> everywhere.
>
> Either method would work - I view the two expressions as synonymous and
> interchangeable. It might be a little different if the uses were
> separated over a longer distance in code rather than being adjacent lines.
>
> And I know that some people think it is nice to write "sizeof(buff)",
> others prefer "sizeof buff". I'd rather not argue too much about style
> here.
Same here. I wrote sizeof buff because that's how I write it. I did
not intend to suggest there was any reason to change or re-hash that old
chestnut. The mixing of the constant and sizeof is slightly more than
style, though, since there's a small cognitive cost to checking the
constant. Even if defined just above, you need to check the spelling in
both usages just in case there's some other similar #define for
something else like my_logPrintBuffLen.
<snip>
--
Ben.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-08 13:41 +0200 |
| Message-ID | <ocai4l$lc8$1@dont-email.me> |
| In reply to | #107832 |
On 07/04/17 20:35, Ben Bacarisse wrote:
> David Brown <david.brown@hesbynett.no> writes:
>
>> On 07/04/17 14:50, Ben Bacarisse wrote:
>>> David Brown <david.brown@hesbynett.no> writes:
>>> <snip>
>>>> #define my_errPrintfBuffLen 256
>>>> #define my_err(f, x...) \
>>>> do { \
>>>> char buff[my_errPrintfBuffLen]; \
>>>> snprintf(buff, sizeof(buff) - 1, f, ## x); \
>>>> buff[my_errPrintfBuffLen - 1] = 0; \
>>>
>>> I was also going to mention this. It seems a bit belt and braces. Is
>>> there any problem with
>>>
>>> snprintf(buff, sizeof(buff), f, ## x); \
>>>
>>> and no extra "buff[my_errPrintfBuffLen - 1] = 0;"?
>>
>> Looks like you are right - snprintf guarantees to put a null character
>> at the end. The history of this macro in my code stretches back to use
>> with older compilers from long ago, where I was less confident in the
>> accuracy of the C library implementation. I can't remember if I
>> actually found a problem with a missing termination, or merely thought
>> it was a possibility.
>
> Yes, that was why I asked. The sn* family predate the C99 standard but
> I don't recall any that would not generate a string (unless the size is
> zero of course). But then my memory is not what it was, so I asked if
> you had had reason to be suspicious.
I can't remember the details of the tools I had at that time. But in
general, I have in the past used quite a number of compilers (and
libraries) that intentionally or unintentionally (i.e., toolchain bugs)
do not follow the rules of C. Some of the fun ones include:
Floating point only working for local variables, not statically
allocated variables.
Uninitialised data that is not zeroed before main().
"const" being treated as meaning "in flash", and using different
assembly instructions for access than ram data - thus casting a "char *"
pointer to a "const char *" pointer will break the program.
Integer promotion rules aimed at being "helpful" to the programmer,
rather than following the standards.
And of course, lots of bugs in compilers and libraries. Many of the
tools I have used have been small ones for odd microcontrollers - they
simply haven't had the levels of use and testing of "big" compilers.
But perhaps the reason I explicitly added a terminating 0 to that macro
was that when I wrote it, I didn't know the details of snprintf well enough!
>
>>> Incidentally, mixing the use of my_errPrintfBuffLen and sizeof buff is
>>> less the ideal. I'd either use the defined constant only in the
>>> declaration and sizeof buff - 1 in both other places, or, if I felt the
>>> need to avoid confusing people with sizeof, I'd use the defined constant
>>> everywhere.
>>
>> Either method would work - I view the two expressions as synonymous and
>> interchangeable. It might be a little different if the uses were
>> separated over a longer distance in code rather than being adjacent lines.
>>
>> And I know that some people think it is nice to write "sizeof(buff)",
>> others prefer "sizeof buff". I'd rather not argue too much about style
>> here.
>
> Same here. I wrote sizeof buff because that's how I write it. I did
> not intend to suggest there was any reason to change or re-hash that old
> chestnut. The mixing of the constant and sizeof is slightly more than
> style, though, since there's a small cognitive cost to checking the
> constant. Even if defined just above, you need to check the spelling in
> both usages just in case there's some other similar #define for
> something else like my_logPrintBuffLen.
>
Fair 'nuff.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-04-10 13:52 -0700 |
| Message-ID | <4f2b7ab6-2fa0-4fc9-aea2-7912adf068b5@googlegroups.com> |
| In reply to | #107858 |
On Saturday, April 8, 2017 at 6:42:10 AM UTC-5, David Brown wrote: > I can't remember the details of the tools I had at that time. But in > general, I have in the past used quite a number of compilers (and > libraries) that intentionally or unintentionally (i.e., toolchain bugs) > do not follow the rules of C. Some of the fun ones include: > > Floating point only working for local variables, not statically > allocated variables. I can't think of any implementations with that restriction, though I recall one that didn't support floating-point types at all. It might possibly have allowed floating-point computations in constant expressions, but if it did I never used that feature. I can't think why only local variables could use floating-point, though. > Uninitialised data that is not zeroed before main(). For some embedded systems, it's essential to have some storage which is not zeroed before main, since various events may cause the system to reset and re-execute main() from scratch but with the contents of memory undisturbed. If a controller has a "power-on" hardware flag which gets set by hardware whenever the voltage falls below a safe operational level and can only be cleared by software when voltage is adequate for operation, software can use that flag to manually initialize things that need to be initialized. The Standard regards as implementation-defined the nature and means by which an embedded program starts operation. It allows systems to define alternate function signatures and startup patterns; I don't know whether a conforming implementation could refrain from initializing global variables before it called "int main(void)", but I think it document the behavior of "void main()" as being similar to "int main(void)" but without zero-initialization. > "const" being treated as meaning "in flash", and using different > assembly instructions for access than ram data - thus casting a "char *" > pointer to a "const char *" pointer will break the program. I somewhat prefer the approach of using different qualifiers for things which are guaranteed to be in RAM, guaranteed to be in code space, or might be in either, but using "const" could be considered a practical approach even though it is non-conforming. > Integer promotion rules aimed at being "helpful" to the programmer, > rather than following the standards. The Standard allows implementations some flexibility to be helpful with regard to signed integers. Unsigned integer types have rigidly-defined behavior.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-11 00:18 +0200 |
| Message-ID | <och05v$le7$1@dont-email.me> |
| In reply to | #108013 |
On 10/04/17 22:52, supercat@casperkitty.com wrote: > On Saturday, April 8, 2017 at 6:42:10 AM UTC-5, David Brown wrote: >> I can't remember the details of the tools I had at that time. But in >> general, I have in the past used quite a number of compilers (and >> libraries) that intentionally or unintentionally (i.e., toolchain bugs) >> do not follow the rules of C. Some of the fun ones include: >> >> Floating point only working for local variables, not statically >> allocated variables. > > I can't think of any implementations with that restriction, though I recall > one that didn't support floating-point types at all. It might possibly have > allowed floating-point computations in constant expressions, but if it did I > never used that feature. I can't think why only local variables could use > floating-point, though. It is highly unlikely that anyone would guess that situation - it was an odd one, due mainly to issues with the hardware on the board that meant access to external memory had problems, while the stack and local variables were on the small internal memory on the device. > >> Uninitialised data that is not zeroed before main(). > > For some embedded systems, it's essential to have some storage which is > not zeroed before main, since various events may cause the system to > reset and re-execute main() from scratch but with the contents of memory > undisturbed. If a controller has a "power-on" hardware flag which gets > set by hardware whenever the voltage falls below a safe operational level > and can only be cleared by software when voltage is adequate for operation, > software can use that flag to manually initialize things that need to be > initialized. If you are relying on the state of memory being preserved at the start of main(), then you are working outside of standard C. There are three common ways to deal with that. One is to use named sections for "no initialisation" data areas for the variables of interest. Another is to fake that by using manually assigned addresses to ram that is outside of the areas used by C. And a third is to put the relevant C code in sections or hooks for execution before the C runtime library clears the bss and sets up the data section. These are, of course, all implementation-specific - but they will be clear and reliable methods. There are some compiler manufactures (such as Texas Instruments) that seem to think it is a good idea not to zero out uninitialised data (the bss section) before main(). Their idea is that this makes startup faster, and that because their hardware watchdogs are often enabled out of reset (a questionable idea at best), they don't zero the bss to avoid a risk of the watchdog triggering before main() starts. It is a silly idea, and has lead to much wailing and gnashing of teeth for developers who find their programs behaving differently from run to run until they learn of this "feature". > > The Standard regards as implementation-defined the nature and means by which > an embedded program starts operation. It allows systems to define alternate > function signatures and startup patterns; I don't know whether a conforming > implementation could refrain from initializing global variables before it > called "int main(void)", but I think it document the behavior of "void main()" > as being similar to "int main(void)" but without zero-initialization. They /could/ document it - but it would then not be C, because C requires this zero initialisation (5.1.2, 6.7.9 p 10). In at least one of TI's compiler manuals, I did see that it was documented - as a tiny footnote, deep within the middle of the manual, with a comment that the behaviour broke with the C standards. > >> "const" being treated as meaning "in flash", and using different >> assembly instructions for access than ram data - thus casting a "char *" >> pointer to a "const char *" pointer will break the program. > > I somewhat prefer the approach of using different qualifiers for things which > are guaranteed to be in RAM, guaranteed to be in code space, or might be in > either, but using "const" could be considered a practical approach even though > it is non-conforming. It was a terrible approach, because it silently broke code that used the good programming practice of using const pointers when a function does not change the data pointed at. It is far better to use explicit extensions, such as a "flash" keyword, for that purpose. > >> Integer promotion rules aimed at being "helpful" to the programmer, >> rather than following the standards. > > The Standard allows implementations some flexibility to be helpful with > regard to signed integers. Unsigned integer types have rigidly-defined > behavior. > No, it does not allow such flexibility. If you have a uint8_t or an int8_t, and you perform arithmetic, it is to be promoted to "int" before carrying out the operation. A compiler that does not do that (logically, at least - it does not actually have to do the 16+ bit operations if only parts of the results are used) is a broken compiler that causes confusion and means code that should be portable and testable on different targets, no longer works. In most cases, these things are done in the mistaken belief that the compiler writer is helping users, or making the tool easy for beginners, or making a more "natural" language for the particular target. But it means that people who know what they are doing, are going to have trouble because the compiler does not work the way they expect. And people who /don't/ know what they are doing, are going to have trouble - because they will refer to books, websites, and expert opinions which are all incompatible with the "helpful" compiler.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-04-10 15:43 -0700 |
| Message-ID | <2937e657-8cbe-40ad-a526-978b29bd9247@googlegroups.com> |
| In reply to | #108020 |
On Monday, April 10, 2017 at 5:18:39 PM UTC-5, David Brown wrote:
> There are some compiler manufactures (such as Texas Instruments) that
> seem to think it is a good idea not to zero out uninitialised data (the
> bss section) before main(). Their idea is that this makes startup
> faster, and that because their hardware watchdogs are often enabled out
> of reset (a questionable idea at best), they don't zero the bss to avoid
> a risk of the watchdog triggering before main() starts. It is a silly
> idea, and has lead to much wailing and gnashing of teeth for developers
> who find their programs behaving differently from run to run until they
> learn of this "feature".
The Keil/ARM approach to that issue is to call a function called SystemInit
before any static-duration variables are initialized, and then initialize
static-duration variables and call main() after that. I would expect that
it would be possible to have SystemInit invoke the main program loop itself
without returning in cases where initialization was known to have happened
previously and should not be repeated, but my preferred approach is to use
separate sections for things that have to survive a reset.
> > The Standard regards as implementation-defined the nature and means by which
> > an embedded program starts operation. It allows systems to define alternate
> > function signatures and startup patterns; I don't know whether a conforming
> > implementation could refrain from initializing global variables before it
> > called "int main(void)", but I think it document the behavior of "void main()"
> > as being similar to "int main(void)" but without zero-initialization.
>
> They /could/ document it - but it would then not be C, because C
> requires this zero initialisation (5.1.2, 6.7.9 p 10). In at least one
> of TI's compiler manuals, I did see that it was documented - as a tiny
> footnote, deep within the middle of the manual, with a comment that the
> behaviour broke with the C standards.
I don't think the Standard allows compilers to regard as Undefined Behavior
the use of any entry-function signature other than those explicitly described.
If an implementation that didn't describe any behavior for "void main()"
would be allowed to treat it as UB, I see no reason that it shouldn't be
allowed to describe a useful behavior that differs from "int main()".
> It was a terrible approach, because it silently broke code that used the
> good programming practice of using const pointers when a function does
> not change the data pointed at. It is far better to use explicit
> extensions, such as a "flash" keyword, for that purpose.
The "const" pointers were treated as a "universal" pointer type that could
access things in either RAM or flash/ROM. A pointer type for things that
were known to be in flash would have improved efficiency in some cases,
though access through flash was generally slow enough that the extra time
to check whether a pointer was a RAM address wasn't a problem. The treatment
meant that a *const* was incompatible with a **, and meant that code which
needlessly applied "const" to pointers would run much more slowly than it
should, but maintained correct behavior in most cases.
Maintaining compatibility with 100% of code would require using two-byte
pointers for everything and generating a function call for every pointer
access. While requiring that code mark everyplace where a one-byte pointer
should be used might have worked, it would have required a lot of code
markup.
> >> Integer promotion rules aimed at being "helpful" to the programmer,
> >> rather than following the standards.
> >
> > The Standard allows implementations some flexibility to be helpful with
> > regard to signed integers. Unsigned integer types have rigidly-defined
> > behavior.
>
> No, it does not allow such flexibility. If you have a uint8_t or an
> int8_t, and you perform arithmetic, it is to be promoted to "int" before
> carrying out the operation. A compiler that does not do that
> (logically, at least - it does not actually have to do the 16+ bit
> operations if only parts of the results are used) is a broken compiler
> that causes confusion and means code that should be portable and
> testable on different targets, no longer works.
A compiler could just as well promote to an even longer type at its
convenience, except in contexts like "sizeof", since operations on the
longer type would behave identically to "int" in all cases where the
Standard would impose any requirements upon the latter. Further, nothing
in the Standard would forbid a compiler where "int" is 32 bits from treating
unsigned mul(unsigned short x, unsigned short y) { return x*y; }
as equivalent to:
unsigned mul(unsigned short x, unsigned short y) { return 1U*x*y; }
Indeed, looking at the C89 rationale, I think the authors of the Standard
would have been shocked at the notion that a compiler for two's-complement
silent-wraparound hardware should do otherwise except perhaps during a
pedantic sanitizing build.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-04-10 16:10 -0700 |
| Message-ID | <ln37dfx3jf.fsf@kst-u.example.com> |
| In reply to | #108022 |
supercat@casperkitty.com writes:
[...]
> I don't think the Standard allows compilers to regard as Undefined Behavior
> the use of any entry-function signature other than those explicitly described.
I can't think of any interpretation of that statement that is both
reasonable and correct. Can you rephrase it, with careful consideration
to what the phrase "undefined behavior" actually means (N1570 3.4.3)?
If you're using the phrase "undefined behavior" in a sense other than
that defined by the standard, please don't bother explaining.
> If an implementation that didn't describe any behavior for "void main()"
> would be allowed to treat it as UB, I see no reason that it shouldn't be
> allowed to describe a useful behavior that differs from "int main()".
If I understand that correctly, I agree. Using prototypes rather
than old-style declarations for greater clarity, if an implementation
doesn't define the behavior of `void main(void)`, then its behavior
is undefined -- and always, the actual behavior might be something
useful, whether it's documented/defined or not. An implementation
is free to define the behavior of `void main(void)` in any way it
likes (as long as it doesn't contradict the rest of the standard)
(5.1.2.2.1p1), and that behavior needn't match the behavior of
`int main(void)`.
(Where "the behavior of `void main(void)`" is verbal shorthand for "the
behavior of a program that defines `void main(void) { /* ... */ }`.)
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-04-10 16:26 -0700 |
| Message-ID | <13ec800d-d0e6-400e-a054-6957d3d9da03@googlegroups.com> |
| In reply to | #108023 |
On Monday, April 10, 2017 at 6:10:19 PM UTC-5, Keith Thompson wrote:
> supercat writes:
> > If an implementation that didn't describe any behavior for "void main()"
> > would be allowed to treat it as UB, I see no reason that it shouldn't be
> > allowed to describe a useful behavior that differs from "int main()".
>
> If I understand that correctly, I agree. Using prototypes rather
> than old-style declarations for greater clarity, if an implementation
> doesn't define the behavior of `void main(void)`, then its behavior
> is undefined -- and always, the actual behavior might be something
> useful, whether it's documented/defined or not. An implementation
> is free to define the behavior of `void main(void)` in any way it
> likes (as long as it doesn't contradict the rest of the standard)
> (5.1.2.2.1p1), and that behavior needn't match the behavior of
> `int main(void)`.
>
> (Where "the behavior of `void main(void)`" is verbal shorthand for "the
> behavior of a program that defines `void main(void) { /* ... */ }`.)
That's my thinking. And I think the initialization behavior would only be
required before a call to one of the forms of "int main()" that would be
described by the Standard; I certainly don't think the authors of the
Standard wanted to forbid the approach used by the Keil compiler (with its
call to SystemInit() before variable initialization), and if SystemInit never
returns, the implementation won't ever call main().
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-11 11:33 +0200 |
| Message-ID | <oci7n4$l8l$1@dont-email.me> |
| In reply to | #108022 |
On 11/04/17 00:43, supercat@casperkitty.com wrote:
> On Monday, April 10, 2017 at 5:18:39 PM UTC-5, David Brown wrote:
>> There are some compiler manufactures (such as Texas Instruments) that
>> seem to think it is a good idea not to zero out uninitialised data (the
>> bss section) before main(). Their idea is that this makes startup
>> faster, and that because their hardware watchdogs are often enabled out
>> of reset (a questionable idea at best), they don't zero the bss to avoid
>> a risk of the watchdog triggering before main() starts. It is a silly
>> idea, and has lead to much wailing and gnashing of teeth for developers
>> who find their programs behaving differently from run to run until they
>> learn of this "feature".
>
> The Keil/ARM approach to that issue is to call a function called SystemInit
> before any static-duration variables are initialized, and then initialize
> static-duration variables and call main() after that. I would expect that
> it would be possible to have SystemInit invoke the main program loop itself
> without returning in cases where initialization was known to have happened
> previously and should not be repeated, but my preferred approach is to use
> separate sections for things that have to survive a reset.
>
Such "SystemInit" hooks are usually used for configuring external
memory, setting up clocks, handling watchdog setup, etc. They /can/ be
used for dealing with data that needs preserved over restarts. But I
agree that separate sections are usually the most convenient method.
>>> The Standard regards as implementation-defined the nature and means by which
>>> an embedded program starts operation. It allows systems to define alternate
>>> function signatures and startup patterns; I don't know whether a conforming
>>> implementation could refrain from initializing global variables before it
>>> called "int main(void)", but I think it document the behavior of "void main()"
>>> as being similar to "int main(void)" but without zero-initialization.
>>
>> They /could/ document it - but it would then not be C, because C
>> requires this zero initialisation (5.1.2, 6.7.9 p 10). In at least one
>> of TI's compiler manuals, I did see that it was documented - as a tiny
>> footnote, deep within the middle of the manual, with a comment that the
>> behaviour broke with the C standards.
>
> I don't think the Standard allows compilers to regard as Undefined Behavior
> the use of any entry-function signature other than those explicitly described.
> If an implementation that didn't describe any behavior for "void main()"
> would be allowed to treat it as UB, I see no reason that it shouldn't be
> allowed to describe a useful behavior that differs from "int main()".
>
I can't figure out what you are trying to say here. We are not talking
about undefined behaviour here - we are talking about a non-conforming
implementation of the compiler.
>> It was a terrible approach, because it silently broke code that used the
>> good programming practice of using const pointers when a function does
>> not change the data pointed at. It is far better to use explicit
>> extensions, such as a "flash" keyword, for that purpose.
>
> The "const" pointers were treated as a "universal" pointer type that could
> access things in either RAM or flash/ROM.
C does not make a distinction between RAM or ROM. /All/ pointers in C
to a given type are "universal" pointers. A const pointer is a pointer
which the /programmer/ promises he will not use to modify data. Data
that is defined as const is stronger - there the compiler /knows/ that
the data will never be modified, and is allowed to place it in read-only
memory.
Of course, a compiler implementation can provide extensions beyond this
model - and perhaps limitations if necessary in order to keep an
efficient implementation. But a compiler implementation may not usurp
normal standard C keywords and usage for such different behaviour.
In this particular case, the microcontroller in question was an AVR.
This is an 8-bit device, and it uses different cpu instructions to
access memory in RAM from memory in Flash. The old compiler (I omit the
name to protect the guilty) was trying to make it convenient for casual
users to have data such as tables or strings in flash. In contrast, the
standard compiler for this chip is gcc which uses normal semantics and
normal standard C. This means that if you write "const int x = 1234;"
and take the address of x, then x will be placed in RAM - even though it
never changes. This is necessary to make pointers work properly, as
normal C. The compiler provides extensions to allow you to put data
specifically in flash and access it directly (including a type of
universal pointer that can access anything).
> A pointer type for things that
> were known to be in flash would have improved efficiency in some cases,
> though access through flash was generally slow enough that the extra time
> to check whether a pointer was a RAM address wasn't a problem. The treatment
> meant that a *const* was incompatible with a **, and meant that code which
> needlessly applied "const" to pointers would run much more slowly than it
> should, but maintained correct behavior in most cases.
>
> Maintaining compatibility with 100% of code would require using two-byte
> pointers for everything and generating a function call for every pointer
> access. While requiring that code mark everyplace where a one-byte pointer
> should be used might have worked, it would have required a lot of code
> markup.
>
>>>> Integer promotion rules aimed at being "helpful" to the programmer,
>>>> rather than following the standards.
>>>
>>> The Standard allows implementations some flexibility to be helpful with
>>> regard to signed integers. Unsigned integer types have rigidly-defined
>>> behavior.
>>
>> No, it does not allow such flexibility. If you have a uint8_t or an
>> int8_t, and you perform arithmetic, it is to be promoted to "int" before
>> carrying out the operation. A compiler that does not do that
>> (logically, at least - it does not actually have to do the 16+ bit
>> operations if only parts of the results are used) is a broken compiler
>> that causes confusion and means code that should be portable and
>> testable on different targets, no longer works.
>
> A compiler could just as well promote to an even longer type at its
> convenience, except in contexts like "sizeof", since operations on the
> longer type would behave identically to "int" in all cases where the
> Standard would impose any requirements upon the latter. Further, nothing
> in the Standard would forbid a compiler where "int" is 32 bits from treating
>
> unsigned mul(unsigned short x, unsigned short y) { return x*y; }
>
> as equivalent to:
>
> unsigned mul(unsigned short x, unsigned short y) { return 1U*x*y; }
>
> Indeed, looking at the C89 rationale, I think the authors of the Standard
> would have been shocked at the notion that a compiler for two's-complement
> silent-wraparound hardware should do otherwise except perhaps during a
> pedantic sanitizing build.
>
You don't need to repeat your favourite bugbear yet again - it is not
relevant here, any more than it is most of the times you bring it up.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-04-11 08:37 -0700 |
| Message-ID | <aa3e8f39-85a0-4c75-9adc-bc79d54005f5@googlegroups.com> |
| In reply to | #108038 |
On Tuesday, April 11, 2017 at 4:33:26 AM UTC-5, David Brown wrote:
> On 11/04/17 00:43, supercat wrote:
> > On Monday, April 10, 2017 at 5:18:39 PM UTC-5, David Brown wrote:
> > I don't think the Standard allows compilers to regard as Undefined Behavior
> > the use of any entry-function signature other than those explicitly described.
> > If an implementation that didn't describe any behavior for "void main()"
> > would be allowed to treat it as UB, I see no reason that it shouldn't be
> > allowed to describe a useful behavior that differs from "int main()".
> >
>
> I can't figure out what you are trying to say here. We are not talking
> about undefined behaviour here - we are talking about a non-conforming
> implementation of the compiler.
If some compiler X doesn't document any particular behavior for "void main()"
but such code happens to behave like "int main()" but without variable
initialization, such behavior would be conforming since use of "void main()"
would be UB, making all possible actions conforming.
If some compiler Y were identical to X except that it *documented* that
"void main()" would work as it does in compiler X, then "void main()" would
not be UB on compiler Y, but I see no reason that compiler Y shouldn't be
conforming.
> > The "const" pointers were treated as a "universal" pointer type that could
> > access things in either RAM or flash/ROM.
>
> C does not make a distinction between RAM or ROM. /All/ pointers in C
> to a given type are "universal" pointers. A const pointer is a pointer
> which the /programmer/ promises he will not use to modify data. Data
> that is defined as const is stronger - there the compiler /knows/ that
> the data will never be modified, and is allowed to place it in read-only
> memory.
I'm well aware of that. The question is whether it's better to have a
compiler:
1. default to RAM-only pointers and require that all objects fit in
RAM unless they have a special qualifier that makes them only
accessible via specially-qualified pointers
2. default to universal pointers and require that any code that needs
to perform well with data in RAM use qualifiers to allow that, or
have the default behavior in the absence of vendor-specific
qualifiers depend upon whether things are "const" qualified. If I
were designing a compiler, I'd be inclined to allow command line
settings or #pragma directives select among such behaviors, since
there are situations where each could be most useful, but the
non-conforming option is probably the one which would allow the
largest amount of code to work efficiently without modification.
> Of course, a compiler implementation can provide extensions beyond this
> model - and perhaps limitations if necessary in order to keep an
> efficient implementation. But a compiler implementation may not usurp
> normal standard C keywords and usage for such different behaviour.
Compilers can do whatever they want. Such behavior may make them non-
conforming, but in some situations a non-conforming compiler might be more
useful than any fully-conforming compiler ever could be. For example, on
an 8-bit processor, a compiler that has a single 32-bit floating-point type
may be more useful than one which can't pass floating-point values to
variadic functions without bundling 48-bit-or-larger math code. If code
never needs anything beyond standard float precision, the extra machine code
for larger types would represent a waste of time and space.
> In this particular case, the microcontroller in question was an AVR.
> This is an 8-bit device, and it uses different cpu instructions to
> access memory in RAM from memory in Flash. The old compiler (I omit the
> name to protect the guilty) was trying to make it convenient for casual
> users to have data such as tables or strings in flash. In contrast, the
> standard compiler for this chip is gcc which uses normal semantics and
> normal standard C. This means that if you write "const int x = 1234;"
> and take the address of x, then x will be placed in RAM - even though it
> never changes. This is necessary to make pointers work properly, as
> normal C. The compiler provides extensions to allow you to put data
> specifically in flash and access it directly (including a type of
> universal pointer that can access anything).
That's certainly a reasonable approach, and a quality compiler should
probably offer it as an option, but for many purposes a non-conforming
option that works like other compilers do may be more useful.
> > A pointer type for things that
> > were known to be in flash would have improved efficiency in some cases,
> > though access through flash was generally slow enough that the extra time
> > to check whether a pointer was a RAM address wasn't a problem. The treatment
> > meant that a *const* was incompatible with a **, and meant that code which
> > needlessly applied "const" to pointers would run much more slowly than it
> > should, but maintained correct behavior in most cases.
> >
> > Maintaining compatibility with 100% of code would require using two-byte
> > pointers for everything and generating a function call for every pointer
> > access. While requiring that code mark everyplace where a one-byte pointer
> > should be used might have worked, it would have required a lot of code
> > markup.
> >
> >>>> Integer promotion rules aimed at being "helpful" to the programmer,
> >>>> rather than following the standards.
> >>>
> >>> The Standard allows implementations some flexibility to be helpful with
> >>> regard to signed integers. Unsigned integer types have rigidly-defined
> >>> behavior.
> >>
> >> No, it does not allow such flexibility. If you have a uint8_t or an
> >> int8_t, and you perform arithmetic, it is to be promoted to "int" before
> >> carrying out the operation. A compiler that does not do that
> >> (logically, at least - it does not actually have to do the 16+ bit
> >> operations if only parts of the results are used) is a broken compiler
> >> that causes confusion and means code that should be portable and
> >> testable on different targets, no longer works.
> >
> > A compiler could just as well promote to an even longer type at its
> > convenience, except in contexts like "sizeof", since operations on the
> > longer type would behave identically to "int" in all cases where the
> > Standard would impose any requirements upon the latter....
> You don't need to repeat your favourite bugbear yet again - it is not
> relevant here, any more than it is most of the times you bring it up.
What about the other point (requoted above): given code like:
int64_t mul_and_add(int32_t x, int32_t y)
{
return x*y;
}
When generating code for a 32-bit processor that includes a 32x32->64
multiply instruction, the Standard would allow a compiler to, at its
leisure, either perform the multiply as 32x32->32 and sign-extend, or
else perform a 32x32->64 computation and return the result as-is? I
don't know of any compilers that would actually promise the latter,
but the Standard would certainly allow a compiler to do so. On a
processor which uses 32-bit "int" on a 64-bit processor, such a promise
would be rather cheap, though code would have to avoid expressions that
mixed uint32_t with int32_t or smaller types to avoid icky semantics.
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-13 00:20 +0200 |
| Message-ID | <ocm91l$nkc$1@dont-email.me> |
| In reply to | #108042 |
On 11/04/17 17:37, supercat@casperkitty.com wrote: > On Tuesday, April 11, 2017 at 4:33:26 AM UTC-5, David Brown wrote: >> On 11/04/17 00:43, supercat wrote: >>> On Monday, April 10, 2017 at 5:18:39 PM UTC-5, David Brown wrote: >>> I don't think the Standard allows compilers to regard as Undefined Behavior >>> the use of any entry-function signature other than those explicitly described. >>> If an implementation that didn't describe any behavior for "void main()" >>> would be allowed to treat it as UB, I see no reason that it shouldn't be >>> allowed to describe a useful behavior that differs from "int main()". >>> >> >> I can't figure out what you are trying to say here. We are not talking >> about undefined behaviour here - we are talking about a non-conforming >> implementation of the compiler. > > If some compiler X doesn't document any particular behavior for "void main()" > but such code happens to behave like "int main()" but without variable > initialization, such behavior would be conforming since use of "void main()" > would be UB, making all possible actions conforming. > > If some compiler Y were identical to X except that it *documented* that > "void main()" would work as it does in compiler X, then "void main()" would > not be UB on compiler Y, but I see no reason that compiler Y shouldn't be > conforming. You are still making no sense. The implementation's way of handling main() cannot be undefined behaviour - it is defined by the implementation. It might not conform to the C standards, but it can never be undefined behaviour. > >>> The "const" pointers were treated as a "universal" pointer type that could >>> access things in either RAM or flash/ROM. >> >> C does not make a distinction between RAM or ROM. /All/ pointers in C >> to a given type are "universal" pointers. A const pointer is a pointer >> which the /programmer/ promises he will not use to modify data. Data >> that is defined as const is stronger - there the compiler /knows/ that >> the data will never be modified, and is allowed to place it in read-only >> memory. > > I'm well aware of that. The question is whether it's better to have a > compiler: > > 1. default to RAM-only pointers and require that all objects fit in > RAM unless they have a special qualifier that makes them only > accessible via specially-qualified pointers > > 2. default to universal pointers and require that any code that needs > to perform well with data in RAM use qualifiers to allow that, or > have the default behavior in the absence of vendor-specific > qualifiers depend upon whether things are "const" qualified. If I > were designing a compiler, I'd be inclined to allow command line > settings or #pragma directives select among such behaviors, since > there are situations where each could be most useful, but the > non-conforming option is probably the one which would allow the > largest amount of code to work efficiently without modification. > /You/ might be inclined to allow weird behaviour, but we already know you have strange ideas about what C is, was or should be. The question /really/ is, is it better to have a compiler: 1. Accept normal C and work the way everyone expects a C compiler to work, allowing good and safe code to work correctly. For some bits of code, extension keywords or other implementation-specific features must be used for optimal efficiency due to limitations of the target architecture. or 2. Change important keywords of C to work in a substantially different way, breaking correct code, because you think it would be easier for beginners to understand this non-standard variant of C. >> Of course, a compiler implementation can provide extensions beyond this >> model - and perhaps limitations if necessary in order to keep an >> efficient implementation. But a compiler implementation may not usurp >> normal standard C keywords and usage for such different behaviour. > > Compilers can do whatever they want. Such behavior may make them non- > conforming, but in some situations a non-conforming compiler might be more > useful than any fully-conforming compiler ever could be. For example, on > an 8-bit processor, a compiler that has a single 32-bit floating-point type > may be more useful than one which can't pass floating-point values to > variadic functions without bundling 48-bit-or-larger math code. If code > never needs anything beyond standard float precision, the extra machine code > for larger types would represent a waste of time and space. > I agree that for some targets, it can be better to make some breakage to C standards conformity. But that should be kept to the absolute minimum, be well documented, not break working code silently, and preferably require a command-line switch to activate it or give warnings about the odd behaviour. It is reasonable, for example, for a C implementation on a small 8-bit micro to make "double" limited to 32 bits. It is /not/ reasonable to change the meaning of a keyword like "const" to mean something subtly different, or to skip the zeroing of uninitialised data. >> In this particular case, the microcontroller in question was an AVR. >> This is an 8-bit device, and it uses different cpu instructions to >> access memory in RAM from memory in Flash. The old compiler (I omit the >> name to protect the guilty) was trying to make it convenient for casual >> users to have data such as tables or strings in flash. In contrast, the >> standard compiler for this chip is gcc which uses normal semantics and >> normal standard C. This means that if you write "const int x = 1234;" >> and take the address of x, then x will be placed in RAM - even though it >> never changes. This is necessary to make pointers work properly, as >> normal C. The compiler provides extensions to allow you to put data >> specifically in flash and access it directly (including a type of >> universal pointer that can access anything). > > That's certainly a reasonable approach, and a quality compiler should > probably offer it as an option, but for many purposes a non-conforming > option that works like other compilers do may be more useful. > No, the "break the const keyword" approach is not more useful. >>> A pointer type for things that >>> were known to be in flash would have improved efficiency in some cases, >>> though access through flash was generally slow enough that the extra time >>> to check whether a pointer was a RAM address wasn't a problem. The treatment >>> meant that a *const* was incompatible with a **, and meant that code which >>> needlessly applied "const" to pointers would run much more slowly than it >>> should, but maintained correct behavior in most cases. >>> >>> Maintaining compatibility with 100% of code would require using two-byte >>> pointers for everything and generating a function call for every pointer >>> access. While requiring that code mark everyplace where a one-byte pointer >>> should be used might have worked, it would have required a lot of code >>> markup. >>> >>>>>> Integer promotion rules aimed at being "helpful" to the programmer, >>>>>> rather than following the standards. >>>>> >>>>> The Standard allows implementations some flexibility to be helpful with >>>>> regard to signed integers. Unsigned integer types have rigidly-defined >>>>> behavior. >>>> >>>> No, it does not allow such flexibility. If you have a uint8_t or an >>>> int8_t, and you perform arithmetic, it is to be promoted to "int" before >>>> carrying out the operation. A compiler that does not do that >>>> (logically, at least - it does not actually have to do the 16+ bit >>>> operations if only parts of the results are used) is a broken compiler >>>> that causes confusion and means code that should be portable and >>>> testable on different targets, no longer works. >>> >>> A compiler could just as well promote to an even longer type at its >>> convenience, except in contexts like "sizeof", since operations on the >>> longer type would behave identically to "int" in all cases where the >>> Standard would impose any requirements upon the latter.... > >> You don't need to repeat your favourite bugbear yet again - it is not >> relevant here, any more than it is most of the times you bring it up. > <snip the needless repetition>
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2017-04-12 15:55 -0700 |
| Message-ID | <5ef4318f-a4f1-4be1-8e9e-bcf6071e6439@googlegroups.com> |
| In reply to | #108083 |
On Wednesday, April 12, 2017 at 5:20:37 PM UTC-5, David Brown wrote: > On 11/04/17 17:37, supercat wrote: > > If some compiler Y were identical to X except that it *documented* that > > "void main()" would work as it does in compiler X, then "void main()" would > > not be UB on compiler Y, but I see no reason that compiler Y shouldn't be > > conforming. > > You are still making no sense. The implementation's way of handling > main() cannot be undefined behaviour - it is defined by the > implementation. It might not conform to the C standards, but it can > never be undefined behaviour. The Standard defines the behavior for "int main(void)". Where does it define any behavior for "void main(void)"? > The question /really/ is, is it better to have a compiler: > > 1. Accept normal C and work the way everyone expects a C compiler to > work, allowing good and safe code to work correctly. For some bits of > code, extension keywords or other implementation-specific features must > be used for optimal efficiency due to limitations of the target > architecture. > > or > > 2. Change important keywords of C to work in a substantially different > way, breaking correct code, because you think it would be easier for > beginners to understand this non-standard variant of C. No implementation will be able to run all correct code. Each of the above approaches will run some programs that the other would not. If some particular program will run usefully on the second approach, but would be unable to run using the first because of resource constraints, then I would suggest the second implementation would be more useful for the purpose of running that program. > I agree that for some targets, it can be better to make some breakage to > C standards conformity. But that should be kept to the absolute > minimum, be well documented, not break working code silently, and > preferably require a command-line switch to activate it or give warnings > about the odd behaviour. It is reasonable, for example, for a C > implementation on a small 8-bit micro to make "double" limited to 32 > bits. It is /not/ reasonable to change the meaning of a keyword like > "const" to mean something subtly different, or to skip the zeroing of > uninitialised data. Most code won't need to convert a const-qualified pointer to a non-qualified pointer and then later convert back to a const-qualified pointer and access ROM/flash. While a compiler should have an option to make all pointers use the same format as const-qualified pointers, I would not expect that most programs would work better with that option disabled. > > That's certainly a reasonable approach, and a quality compiler should > > probably offer it as an option, but for many purposes a non-conforming > > option that works like other compilers do may be more useful. > > No, the "break the const keyword" approach is not more useful. If a program would work correctly with or with or without that option, but be faster without, or if it would work correctly with that option but would not fit in the available code space without it, in what way would that option make the implementation more useful for purposes of running that program? If the Atmel can't use a const-qualified pointer to access RAM (I've not used that platform, so I don't know if it can), that would be a horribly broken abuse of the "const" keyword, but if "const"-qualified pointers can access either while unqualified pointers are limited to RAM, most programs would not be affected by an inability to round-trip const pointers through unqualified pointers nor directly use unqualified pointers to access ROM.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2017-04-12 18:17 -0700 |
| Message-ID | <lnpoghumwg.fsf@kst-u.example.com> |
| In reply to | #108087 |
supercat@casperkitty.com writes:
> On Wednesday, April 12, 2017 at 5:20:37 PM UTC-5, David Brown wrote:
>> On 11/04/17 17:37, supercat wrote:
>> > If some compiler Y were identical to X except that it *documented* that
>> > "void main()" would work as it does in compiler X, then "void main()" would
>> > not be UB on compiler Y, but I see no reason that compiler Y shouldn't be
>> > conforming.
>>
>> You are still making no sense. The implementation's way of handling
>> main() cannot be undefined behaviour - it is defined by the
>> implementation. It might not conform to the C standards, but it can
>> never be undefined behaviour.
>
> The Standard defines the behavior for "int main(void)". Where does it define
> any behavior for "void main(void)"?
If "void main(void)" is documented as "some other implementation-defined
manner" as described in 5.1.2.2.1p1, its behavior (other than its
termination status) is defined by the entire standard.
[...]
> No implementation will be able to run all correct code. Each of the above
> approaches will run some programs that the other would not. If some
> particular program will run usefully on the second approach, but would be
> unable to run using the first because of resource constraints, then I would
> suggest the second implementation would be more useful for the purpose of
> running that program.
Any acceptable implementation will be useful for more than the purpose
of running one specific program.
There are ways to define extensions without voilating the standard, such
as "#pragma".
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Date | 2017-04-14 12:27 +0200 |
| Message-ID | <ocq815$4ud$1@dont-email.me> |
| In reply to | #108087 |
On 13/04/17 00:55, supercat@casperkitty.com wrote: > On Wednesday, April 12, 2017 at 5:20:37 PM UTC-5, David Brown wrote: >> On 11/04/17 17:37, supercat wrote: >>> If some compiler Y were identical to X except that it *documented* that >>> "void main()" would work as it does in compiler X, then "void main()" would >>> not be UB on compiler Y, but I see no reason that compiler Y shouldn't be >>> conforming. >> >> You are still making no sense. The implementation's way of handling >> main() cannot be undefined behaviour - it is defined by the >> implementation. It might not conform to the C standards, but it can >> never be undefined behaviour. > > The Standard defines the behavior for "int main(void)". Where does it define > any behavior for "void main(void)"? I still don't get your hang-up about main() here, or your idea of the implementation being undefined behaviour. The standards make it clear that program lifetime objects without explicit initialisation are zero'ed before the start of the program. A freestanding C implementation can use whatever it likes as a start function - it can be "void main(void)", or "int reset(int)", or whatever it wants. That is for the implementation to decide. And if the programmer uses a form that is not specified by the implementation, then /that/ is undefined behaviour. > >> The question /really/ is, is it better to have a compiler: >> >> 1. Accept normal C and work the way everyone expects a C compiler to >> work, allowing good and safe code to work correctly. For some bits of >> code, extension keywords or other implementation-specific features must >> be used for optimal efficiency due to limitations of the target >> architecture. >> >> or >> >> 2. Change important keywords of C to work in a substantially different >> way, breaking correct code, because you think it would be easier for >> beginners to understand this non-standard variant of C. > > No implementation will be able to run all correct code. Each of the above > approaches will run some programs that the other would not. If some > particular program will run usefully on the second approach, but would be > unable to run using the first because of resource constraints, then I would > suggest the second implementation would be more useful for the purpose of > running that program. > Clearly it is possible to write code that can be run by the second implementation. And clearly it is possible to write code that /cannot/ be run by the first implementation (just use enough constant data to overflow the ram in the microcontroller, avoiding the implementation-specific features needed to keep that constant data in flash). But with the second implementation, standard programs that should be able to run (i.e., they are within the limitations of ram, rom, etc.), will compile cleanly but have different and incorrect behaviour compared to other conforming C implementations. And that is the vital difference. >> I agree that for some targets, it can be better to make some breakage to >> C standards conformity. But that should be kept to the absolute >> minimum, be well documented, not break working code silently, and >> preferably require a command-line switch to activate it or give warnings >> about the odd behaviour. It is reasonable, for example, for a C >> implementation on a small 8-bit micro to make "double" limited to 32 >> bits. It is /not/ reasonable to change the meaning of a keyword like >> "const" to mean something subtly different, or to skip the zeroing of >> uninitialised data. > > Most code won't need to convert a const-qualified pointer to a non-qualified > pointer and then later convert back to a const-qualified pointer and access > ROM/flash. While a compiler should have an option to make all pointers use > the same format as const-qualified pointers, I would not expect that most > programs would work better with that option disabled. The problem is non-const data accessed through const qualified pointers. And such code /is/ common: extern int average(int noOfSamples, const int * pSamples); int x[10]; int av = average(10, x); > >>> That's certainly a reasonable approach, and a quality compiler should >>> probably offer it as an option, but for many purposes a non-conforming >>> option that works like other compilers do may be more useful. >> >> No, the "break the const keyword" approach is not more useful. > > If a program would work correctly with or with or without that option, but > be faster without, or if it would work correctly with that option but would > not fit in the available code space without it, in what way would that > option make the implementation more useful for purposes of running that > program? A C compiler should not be made to work only with a particular specialised variety of programs! A compiler sold as a "C Compiler" should be made to correctly compile as many valid C programs as possible given the limitations of the target. > > If the Atmel can't use a const-qualified pointer to access RAM (I've not > used that platform, so I don't know if it can), that would be a horribly > broken abuse of the "const" keyword, Exactly. (The limitation is for one particular compiler for the AVR, not for all compilers for it.) > but if "const"-qualified pointers > can access either while unqualified pointers are limited to RAM, most > programs would not be affected by an inability to round-trip const pointers > through unqualified pointers nor directly use unqualified pointers to > access ROM. > Certainly that would be much less of a problem.
[toc] | [prev] | [next] | [standalone]
Page 1 of 3 [1] 2 3 Next page →
Back to top | Article view | comp.lang.c
csiph-web