-3

compiler context

First off, I'm currently using Microsoft Visual Studio Community 2022 (64-bit) - Current Version 17.7.5. (Just in case this question is more about the tools I'm using than the language and I'm mistaken when suggesting this is about the c language itself.)

question foundation

Case 1 below gives this error: expression must be a modifiable lvalue

/* case 1 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char* defaultlist[] = { "string 1", "string 2" };
char** getlist() { return NULL; }
int main( int argc, char* argv[] ) {
    char* list[]= getlist();      /* line with declaration I'm accustomed to using in past */
    if ( list == NULL )
        list = defaultlist;       /* line with error message */
    return 0;
}

Case 2 is fine:

/* case 2 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char* defaultlist[] = { "string 1", "string 2" };
char** getlist() { return NULL; }
int main( int argc, char* argv[] ) {
    char** list= getlist();       /* modified declaration */
    if ( list == NULL )
        list = defaultlist;       /* no error, anymore */
    return 0;
}

In my ancient past there was no semantic difference whatsoever between a declaration that says char** p and one that says char* p[]. They had identical semantics in all declaration contexts in the 1970's, 1980's, and 1990's when I spent more time with c, than of late. (If I'm wrong about that, I'd like a stern correction and an example demonstrating the difference.)

I'm now observing a complaint from the compiler that suggests a new semantics I've not been accustomed to, previously. It seems that now, c compilers have decided that char* list[] is really char* (* const list). Which isn't my intention.

question

The following points should be addressed:

  • Am I mistaken in asserting the declaration semantics of char** p and char* p[] were at some point identical? If so, and if it has always been different even back to the 1978 when I first learned C working on the Unix v6 kernel code, then exactly how has it been different and how might I have detected the difference had I tried to do so? (I have many old compilers here that I can use for testing purposes.)
  • Am I correct in recognizing that the declaration semantics of char** p and char* p[] are now different in the sense that char* p[], in certain circumstances I've yet to learn about, are actually taken as char* (* const p) and that this change occurred in some revision to the c standard? (Or some other semantic meaning I missed considering?)
  • When was the change made, assuming I am right that a change was made in the standard?
13
  • 4
    "In my ancient past there was no semantic difference whatsoever between a declaration that says char** p and one that says char* p[]" – when they are function arguments. Commented Mar 21, 2024 at 20:36
  • @WeatherVane I was using that example only to show that there are two different contexts. I could just as well completely remove it and the question would still stand as before. In fact, let me do that and remove it. Point as been removed from the question so that it won't act as a red herring. Commented Mar 21, 2024 at 20:37
  • 4
    char* list[] is an array of pointers. It can't be initialised with a function call (except with braces), list will never be NULL, and list = defaultlist; is an invalid assignment. Commented Mar 21, 2024 at 20:40
  • 2
    It think the onus is on you to show the exact syntax that compiles, and the name and version of the compiler. Obviously it is not Case 2, where you changed the syntax. Perhaps your memory is of using char* list[] = { getlist() }; but char** getlist() { return NULL; } would then be incorrect: one star char* getlist() { return NULL; } Commented Mar 21, 2024 at 20:45
  • 2
    In Standard C, you have never been allowed to assign to an array name when the array is defined at file scope or in a function body. You could appear to assign to an array name when the array was a function parameter, because the array was treated as a pointer. I'm with @WeatherVane on this. I've not encountered a compiler in my last 42 years of C programming that allowed assignments to a plain (non-parameter) array name (though I've not tested for the (mis)feature) and I've used quite a variety of compilers. If MS 1.52c allowed it, it was aberrant and not following the C standard. Commented Mar 21, 2024 at 21:03

2 Answers 2

5
  • Am I mistaken in asserting the declaration semantics of char** p and char* p[] were at some point identical? If so, and if it has always been different even back to the 1978 when I first learned C working on the Unix v6 kernel code, then exactly how has it been different and how might I have detected the difference had I tried to do so? (I have many old compilers here that I can use for testing purposes.)

char **p and char *p[] always were and still are equivalent for declaring function parameters. In that context, they are also equivalent to char *p[1] and char *p[10000]. This is because a declaration of a function parameter as an array is automatically "adjusted" to be a declaration of that parameter as a pointer to the erstwhile array's element type. This dovetails with the fact that arrays presented as function arguments (or appearing in most other contexts in expressions) are automatically converted to pointers in an analogous way. These details are described in the 1st edition of K&R (copyright 1978), and they were already well established by then.

  • Am I correct in recognizing that the declaration semantics of char** p and char* p[] are now different in the sense that char* p[], in certain circumstances I've yet to learn about, are actually taken as char* (* const p) and that this change occurred in some revision to the c standard?

char **p and char *p[] were never equivalent for declarations other than function parameters. The former declares a pointer to a char *. The latter declares an array of char * of unspecified length. Given the extent of your experience with C, I would have supposed that you understood well that although they are related, arrays and pointers are completely different kinds of objects. Your remark about char* (* const p) leaves me a bit uncertain about that, though.

All this too is documented in the original K&R, and was well established before then.

  • When was the change made, assuming I am right that a change was made in the standard?

All versions of ISO C agree with K&R C on these points.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, John. I'm actually glad that no one is giving me any room on this. So. I'll go grab up my K&R (still have my copy from when I worked on Unix v6, in fact) and look to see. You wouldn't happen to have a page number in mind?
Nicely done John!
@periblepsis, for array declarations, including with empty brackets, see section 8.4, which is pp. 194-195 in my (relatively late) printing of K&R 1ed. For function parameter adjustments, see the end of section 10.1, p. 205 in my printing.
@JohnBollinger Thanks. I'll go check it. The code was copied from a working program that did compile fine under 1.52c from Microsoft. So I know it works and that compilers once accepted it. I'm using old code and porting it, getting new errors now. This process has helped me a lot. I will prepare a better-formed question when I get the time, should I still have a remaining question. (I had expected different results and that's my fault. More research was appropriate and I failed there.) Would you suggest I keep the question in place? Or delete it? Thanks.
@periblepsis, I can't speak to extensions that random compilers may have exhibited or divergent interpretations of the language they may have taken or bugs that they may have had. I can believe that there have been compilers that accepted your first code, and maybe even did with it what was intended. I am not prepared to believe that there was any time when C compilers did so generally. And for what it's worth, I have never been fond of Microsoft's C compilers.
3

In my ancient past there was no semantic difference whatsoever between a declaration that says char** p and one that says char* p[]. They had identical semantics in all declaration contexts in the 1970's, 1980's, and 1990's when I spent more time with c, than of late

It was never the truth. The difference is the same from the beginning of the C language.

  • char **p; declares a pointer to pointer to char.
  • char* defaultlist[] = { "string 1", "string 2" }; defines the array of two pointers to char.

Arrays can't be assigned (ie used as lvalues), pointers can.

Both lines will not compile:

char* list[]= getlist(); 
list = defaultlist; 

Your confusion is a widespread beginner's misunderstanding of arrays and pointers.

Arrays decay to pointers when used in as rvalues, but they are not pointers. Same happens when you pass the array to a function - you actually pass the pointer to the first element of the array). The confusion comes from the way you can declare function parameters:

void foo(int arr[], int *arr1[]);

They look like arrays but they are pointers.

When was the change made, assuming I am right that a change was made in the standard?

It was always like this. Arrays and pointers were always not the same

char* p[], in certain circumstances I've yet to learn about, are actually taken as char* (* const p)

No, you are 100% wrong. You cant assign arrays, but not because they are const

Am I mistaken in asserting the declaration semantics of char** p and char* p[] were at some point identical?

No, they were never the same.

You are probably confused by this ancient way of declaring parameters:

void foo(p)
int p[];
{
    int arr[5];
    p = arr;
}

It looks like you assign array p, but p (because it is a parameter) is a pointer despite the confusing syntax.

6 Comments

Thanks. But the ability to make these assignments to locally allocated variables existed. Since c didn't get a standard until the 1980's (other than the 1978 book), it's possible that I'm missing something. But that will be for me to prove, I guess. I'll take that on as this is my question and I'm interested in a specific kind of answer.
@periblepsis no - you cant prove it. Arrays are not assignable. You probably are confused by initialization which looks like assignment, but it is not. The C language never allowed it. You can assign structs and unions, but not arrays.
Actually the code was copied from a working program that did compile fine under 1.52c from Microsoft. So I know it works and that compilers once accepted it. (I'm using some old code and porting it.) But this process has, in a very few minutes, helped me a lot. I will prepare a better-formed question when I get the time. I had expected different results and that's my fault. More research was appropriate and I failed there. Would you suggest I keep the question in place? Or delete it? Thanks.
@periblepsis not for sure. Deleting questions with answers (people made an effort and spent their time answering them) is one of the worst habits here.
I'll keep it then and select an answer, instead. Thanks.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.