Empty value detection for char* type variable

Hi all,

I just simply want to detect whether a char * variable is empty or not.

For example,

char* token ="";
If( token != '\0' ){
Serial.println("okay");
}else{
Serial.println("Something is wrong!!!!");
}

But it will alwasy print "okay". What's happening now here? What is the correct way to detect an empty char* variable?

Token contains a pointer to an address and is never empty it doesn’t contain a char. De-reference ?

1 Like

try

if (NULL == *token)

there's a difference between the pointer pointing to nothing, it's value is zero and it pointing to a NULL string (i.e. "") where the first char in the string it is pointing to is NULL (i.e '\0')

variable of the type char* contains a pointer and has nothing to do with string contents

that's bad. You have a pointer to non constant char data and you assign a pointer to constant char data... The compiler will bark (at least on an ESP, the AVR options are more lenient)

for your test you want to do

If( *token != '\0' ){

to test the content of the byte you point to.

char emptyChar = '\0';
char* token = & emptyChar;

If( *token != emptyChar ){
  Serial.println(F("the cString you point at is not empty"));
}else{
  Serial.println(F("the cString you point at is empty"));
}

note that this initialization form is allowed to constant strings only. So your token never changed and will be always contains the empty string and your if condition is completely non-sense

C++ is case sensitive

Just to recap and simplify a bit the suggestions others gave you, you first need to specify what "empty" means for you.
A "char*" is a pointer, thus "empty" pointers are NULL (a "zeroed" address), so you test it by comparing it to NULL:
if (token == NULL)
An "empty string" is a char* pointer (so it's not NULL) pointing to a zero (0x0 or '\0') byte, and you test it by comparing the first pointed byte:
if (*token == 0)

For example:

char * pnull = 0;
char * pempty = "";
char * pnotempty = "Hello world";

void setup() {
  Serial.begin(9600);
  checkString(pnull);
  checkString(pempty);
  checkString(pnotempty);
}

void checkString(char * p) {
  if (p == NULL) {
    Serial.println("NULL");
    return;
  }
  if (*p == '\0') 
    Serial.println("Empty string!");
  else {
    Serial.print("String: \"");
    Serial.print(p);
    Serial.println("\"");
  }
}

void loop() {
}

your summary is not really great, you missed the point that "" is a constant cString, so you can't assign its pointer to a non constant cString. Also 0 should be discouraged in C++ for the null pointer in favor of nullptr.

you should do

char nullChar = '\0';
char * pnull = nullptr;
char * pempty = & nullChar;
1 Like

confusing
can you explain nullptr and the difference between & nullChar and ""? (i think you can)

nullchar is a character you can modify ( one byte is allocated in SRAM and initialized with the value we chose, and you can change it afterwards)

"" is an empty constant cString (string littéral) as such you are not allowed to change the value of the content which the compiler may elect to place in flash memory.

Thus it’s legit to say

char * pempty = & nullChar

Otherwise you would have to write

const char * pempty = ""; // notice the const 

nullptr is the correct way in C++ to denote a pointer pointing at nothing.

is there confusion about a const char and a char string that are null

consider

                    s addr 0xffffcbd0 - 11 68 65 6c 6c 6f 20 77 6f 72 6c 64
                p = 0 addr 0x0
             p george addr 0x100403024 -  6 67 65 6f 72 67 65
                p = s addr 0xffffcbd0 - 11 68 65 6c 6c 6f 20 77 6f 72 6c 64
             s zeroed addr 0xffffcbd0 -  0

#include <stdio.h>
#include <string.h>

void
dispStr (
    char *q,
    const char *label)
{
    printf (" %20s", label);
    printf (" addr %p", q);

    if (q)  {
        printf (" - %2d", strlen (q));
        for (unsigned n = 0; n < strlen (q); n++)
            printf (" %02x", q [n]);
    }
    printf ("\n");
}

int
main ()
{
    char s [80] = "hello world";
    dispStr (s, "s");

    char *p = 0;          // a null ptr
    dispStr (p, "p = 0");

    p = (char*) "george";
    dispStr (p, "p george");

    p = s;
    dispStr (p, "p = s");

    memset (s, 0, sizeof(s));
    dispStr (s, "s zeroed");
}

This is not a legit cast. It will work on AVR because the compiler does not store string literals in flash.

it works on my laptop using gcc

I can’t test right now but What happens if you do

*p = ‘G’;

After assigning george to p

Also why did you take care of defining the second parameter of your function as const char *label ➜ why const if you don’t need it for string literals?

not sure what your asking for?
the dispStr() expects a null terminated string.
also assume you mean p = &'G'. do you just want to see the value (addr) of p

i got a warning

main.cpp:24:17: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
   24 |     dispStr (s, "s");

but why do you say "if you don't need it for string literals"?

i'm not sure i fully understand the motivation for the C++ thing about "const char". (i just noticed that it's just a warning).

main.cpp:29:10: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
   29 |     p =  "george";

but presumably it tells the compiler that the string it not to be modified. unlike other parameters passed to a function by value, while the string ptr may be passed by value, it exposes the string to manipulation in the function. by defining it as "const char", you're asking the compiler to generate an error(?) if there's code that does modify it.

i get following if i define q as const char in distStr() and attempt to change the string

main.cpp: In function ‘void dispStr(const char*, const char*)’:
main.cpp:17:8: error: assignment of read-only location ‘* q’
   17 |     *q = 0;

actually both parameters should be defined as const char

so a string passed to a function that is simply a label should be defined as const char since most of the time that is what is passed

am i explaining this clearly enough ?

I was asking you to try to change the lower case g from “george” into G and print it again.
It is possible that nothing would happen

String literals are constant, depending on the architecture smart compiler would store those strings into non modifiable memory segments, could be flash memory.
Also as they are constant, the compiler is free to optimize the memory and reuse substrings if it identifies some. Try this

On AVR the compiler does store the data in SRAM but the optimizer notices that it can reuse some of the memory for the various values. As they are supposed to be const that’s fine, but if you use the pointer to modify the SRAM for the content pointed by p1, you’ll see that you actually modified the three pointed values. The memory was shared.
That’s why you need const and then the compiler won’t let you do dangerous stuff

p = "George";

yes, same results as "george" no the 6 is the length, the 0x67 changed to 0x47

                    s addr 0xffffcbd0 - 11 68 65 6c 6c 6f 20 77 6f 72 6c 64
                p = 0 addr 0x0
             p george addr 0x100403024 -  6 67 65 6f 72 67 65
                  p G addr 0x100403034 -  6 47 65 6f 72 67 65
                p = s addr 0xffffcbd0 - 11 68 65 6c 6c 6f 20 77 6f 72 6c 64
             s zeroed addr 0xffffcbd0 -  0

certainly doable.

did you notice that the address of the constant char was significantly different that the chars (i.e. text segment)?
you don't by my explanation?

depends on what you think is dangerous

i guess self-modifying code would be frowned on (can save memory if memory is tight)

I did not mean assigning the string with a capital

I really meant trying to use the pointer (as you did not declare it as pointing to const data) to modify the first letter ➜ literally

*p = 'G';

And print again


did you notice that the address of the constant char was significantly different that the chars (i.e. text segment)?

It is architecture dĂ©pendant on where you store the data. For exemple on AVR the compiler keeps it in SRAM because of the Harvard architecture and the need for specific functions to extract data from “PROGMEM” that would break many functions if it were to make the decision to move the string in flash.
On some OSes you could have the string in a non modifiable / read only segment.


No it’s just that the C++ rule states that "george" is a constant string literal and the compiler has this expectation. If you do funny things then with the data because you did not use the const qualifier for the pointer then you are in uncharted territory.

So long story short respect the types and the modern standards

  • If your pointer needs to point at modifiable data then use such a pointer hence my
char nullChar = '\0';
char * pempty = & nullChar;

Or use the null pointer which works for everything

char * pnull = nullptr;
  • if you use const data then declare the pointer accordingly
const char * pEmpty = "";

or use

 char pEmpty[] = "";

Which leads to memory allocation in SRAM

If you don’t respect the const qualification then the compiler will bark at you with
warning: ISO C++ forbids converting a string constant to ‘char*’
or possibly even stop depending on stricter compiler’s option.

Note: it used to be fine if I remember until C++11 to cast and C has allowed it as well due to so much code depending on this.

Arduino is using a C++ compiler so what we know about C does not always apply. C++ is stricter on types.

I have thought that the "" contains one ASCII-coded character which is a null-character. For example:

char *token = "";

In the above declaration, a null-character (the null-byte: 0x00) is placed in the memory location whose base address is in the pointer variable token.

In the following declaration, three ASCII characters (a, b, and null) would be placed in the indexed space.

char *token = "ab";

Would appreciate to hear comments.