Is it possible to have a pointer literal?
Asked Answered
K

4

12

In C one can have string literals in the form of

char *string = "string here";

integer literals:

uint8_t num = 5;

long literals:

long long bigNum = 90322L; 

floating point literals:

float decimal = 6.3f;

Is the a way to have a pointer literal? That is a literal address to a memory space. I am doing some work on an embedded project and need to hard code a value for a DMA access. I am doing something similar to the following:

uint32_t *source = 0x08000000;

While this compiles and works correctly I get the following compiler error (I'm using a variant of GCC):

cc0144: {D} warning: a value of type "int" cannot be used to initialize an entity of type "uint32_t *"
cc0152: {D} warning: conversion of nonzero integer to pointer

Is there a correct way to do this or do I just need to accept this as a fact of C? I know I can do:

uint32_t *source = (uint32_t *)0x08000000;

But that just seems very unnecessary. What is the industry way of doing this? I am also wondering if this feature exists in C++.

Kafka answered 13/6, 2014 at 19:42 Comment(11)
"But that just seems very unnecessary" May I ask why you think that's unnecessary?Ortega
In C++11 you could write a user-defined literal operator, if you so wished.Hives
A null pointer constant is an address literal. There are no literals for non-null pointers. An integer constant with a cast like (uint32_t *)0x08000000 is the right way to do it -- if that's a valid address on your system.Sweeney
@Ortega it seemed unnecessary because I just assumed there would be a built in feature to do this. I felt people need to do this frequently enoughKafka
@Kafka You mean a built-in feature to write fixed addresses? Not a standard feature, no. The address-format is dependent on the architecture.Ortega
@secretformula: I've never had to do this. The only reason I've ever heard for doing it was writing a program that altered memory in another program, or some form of DLL injection.Paschall
@MooingDuck Its a very low level program, i'm using analog devices blackfin architecture. I believe theres a way to do it using sections but it wasn't worth the time in this case to figure out how to do it.Kafka
@Kafka Ah right, I forgot that hardware has a need of this too. That's valid.Paschall
Does the compiler complain if you treat it as a void* ?Thebault
@secretformula: If you're doing low-level stuff, you will eventually need to figure out how to use linker scripts. All of the serious stuff I've seen does it. And defines all their memory locations there, not hard coded in C. Or they come from a device tree, or some other kind of device description table.Nonna
@ZanLynx do you have any good references that cover this?Db
D
14

In both C and C++ the only pointer literal or constant is zero. We can go to the draft C99 standard section 6.3.2.3 Pointers:

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.55)

and:

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.56)

the correct way to deal with non-zero integer constant is to use a cast.

The equivalent section from the draft C++ standard would probably be section 5.2.10 Reinterpret cast which says:

A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined. [ Note: Except as described in 3.7.4.3, the result of such a conversion will not be a safely-derived pointer value. —end note ]

You need to see section 3.7.4.3 for all the details.

For the pointer literal reference you need section 2.14.7 Pointer literals which says:

The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See 4.10 and 4.11. —end note ]

Db answered 13/6, 2014 at 19:54 Comment(0)
N
3

Is there a correct way to do this? ... I know I can do:

uint32_t *source = (uint32_t *)0x08000000;

That is the "correct" way to do it in C.

In C++ you should prefer:

uint32_t * source = reinterpret_cast<uint32_t*>(0x08000000);
Nadabb answered 23/5, 2020 at 19:2 Comment(0)
H
2

No, it's not. That is because literals are valid values, and the only valid pointers are addresses of objects, i.e. the result of address-of operations or of pointer arithmetic on valid pointers.

You could argue that the nullptr keyword furnishes a kind of "pointer literal"; the C++ standard calls it that. It is however the only pointer literal, and ironically it is not of pointer type.

Hives answered 13/6, 2014 at 19:45 Comment(11)
[lex.nullptr]/1 "The pointer literal is the keyword nullptr." (where "pointer-literal" refers to the nonterminal pointer-literal)Ortega
-1: Disagree. I think the right answer was given by Shafik Yaghmour. The "literal" 0 can be used to represent a null pointer in standard C.Malisamalison
@pablo1977: No. That's not what "literal" means at all. The literal 0 has type int, no ambiguity there. It is also a constant expression of value zero, and it can be used to initialize a pointer. But that's a very different thing from being a literal. A literal must an expression of the desired type. (For what it's worth, the accepted answer is nice, but pretty much besides the point.)Hives
@KerrekSB: I disagree with you again. A "literal" is intended to be a language "token", so it has not any type at all. A string literal has no type char*. First, it is a sequence of certain characters (belonging to the source environment) enclosed in double-quotes. This token is recognized by the preprocessor, so the character string literal has not type. In a later stage is converted to an object in memory having type char*. However, I have to concede that you are right with the null-pointer-constant issue. It is considered a constant integer expression, which has an integer type.Malisamalison
@pablo1977: a string literal has type "array of const char" (or suitable wide char).Hives
@KerrekSB: In your answer you says: "literals are valid values". I think that a "literal" is just a token. You are making there the same kind of confusion as secretformulas does, I mean: merging the idea of "literal" (a token) with the concept of "constant expression", which has a type, as you well said in your comment.Malisamalison
@KerrekSB: Of course a string literal has type char*, but in the very definition, a string literal is a syntactical entity. It is defined syntactically. After preprocessing stage 7 the string literal is starting to be considered an object in memory, with elements of type char.Malisamalison
@pablo1977: Yeah, I see what you mean. Literals are first and foremost lexical tokens, but many of them do actually have types right there and then (see 2.14, e.g. integer literals have types). However, note that 5.1.1 says that "a literal is a primary expression", and the expression has a type, and so I hope this identification is appropriate.Hives
@pablo1977: If a string literal had type char *, how would you explain this std::extent resul?Hives
@KerrekSB: Oh! Interesting. I better know the C standard, where "literal" is not defined. There are only "string literals". The rest of "literals" appear just as "constants". You are citing 5.1.1 from C++11 standard, where "literal" is defined as a primary expression, having a type. That's fine. Now I want to drop my -1 vote in your answer, but it would be necessary that you make some modification to your answer, because the system has locked my vote.Malisamalison
As someone who works in the embedded world. I strongly disagree with this statement. The other valid address of pointers is embedded device memory space. For peripherals etc....Caitiff
C
1

Alternative solution:

Stick your address in your linker script (*.ld) And than declare it as an external value:

Its const, you don't need to cast a value. And if you change hardware, your linker script will probably need to be changed anyways, but your firmware might not need to be modified (except the read/write addresses assuming it was well designed, and with those addresses in the linker script, they don't need to be changed.).

Your linker script would look something like this:

MEMORY
{
    ROM (rx) : ORIGIN = 0, LENGTH = 256k
    RAM (wx) : org = 0x00100000, len = 1M
}

.text : (SIZE big enough to fit firmware + 50% for bug fixes, new features etc...)
{ //contains instructions
   KEEP(*(.text.ivt)) //assuming arm, stick your interrupt vector table at offset 0x00000000, KEEP tells the compiler to not prune this, despite it "not" being referenced
   *(.text.boot) // boot code is next, cuz that is what the processor will start executing after loading the vector table.
   *(.text*)     // non boot code, organized however the compiler feels like
} > ROM  // Sticking it in ROM.


.bss  //address statically allocated variables
.data // Semi permanent configurations etc... Sized to be big enough to fit current configuration variables + space for updates/patches... (So that new firmware can keep the old configurations)..
.new_firmware_update_file : SIZE( big enough to fit your current firmware + 50% or more for patches, bug fixes, new features etc... )
.stack(NOLOAD) // define where your stack is/how big it is, alignment etc...
.heap (NOLOAD) // define where your heap starts/how big it is, alignment etc...


// Then you can do other symbols... I like sticking them at the end.
MY_POINTER = 0x80000000; //arbitrary symbol in the linker script, has extern linkage.

You can then use it in your firmware by declaring the linker symbol as an extern variable.

extern volatile const uint32_t * const MY_POINTER; // readonly
extern volatile uint32_t * const MY_POINTER;       // read or write

Probably other ways to do this. This is how I have seen it done.

Caitiff answered 8/7 at 21:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.