This question already has answers here:
Are negative array indexes allowed in C?
(9 answers)
Closed 5 years ago.
I just want to know is it a good way of programming style.
I know what is happening in this piece of code. look for the first occurrence of href save it next_next and then look for the first occurrence of "}" and save it end_marker.
Here my question is end_marker[-1] = '\0'; is needed? Because strstr, upon successful completion, strstr() shall return a pointer to the located string or a null pointer if the string is not found.
I know the endmarker '\0' is for string but don't know is it good to index the array in the negative number?
Code:
char *end_marker;
char *next_next = strstr(links_ptr, "href");
if (next_next != NULL) {
next_next += 7;
end_marker= strstr(next_next, "}");
end_marker[-1] = '\0'; // :)
}
EDIT: links_ptr contains this data
"links": [
{
"rel": "next",
"href": "https://www.randomstuff.com/blabla"
}
]
This usage of strstr assumes much about the input. Given input it doesn't expect, it can scan memory out of the string bounds, write to bad addresses, or try to dereference a null pointer.
If links_ptr is different - if it's part of user input or data downloaded on the internet - then it's a definite bug and security issue.
next_next += 7 assumes that strlen(next_next) >= 7. If the string is shorter you'll be scanning memory that doesn't belong to the string until the first '\0' or '}' is found.
if the previous scan finds '}' it will write '\0' to an unrelated address
if '}' isn't found, end_marker will be NULL and end_marker[-1] should crash
In C/C++, there's nothing evil in using a negative array index. In this way you are addressing the slot BEFORE the pointer represented by end_marker. However, you need to ensure that there's valid memory at this address.
In this case it would be undefined behaviour, you should do
if (end_marker != NULL)
{
end_marker[strlen(end_marker) - 1] = '\0';
}
Using negative number isnt good practice and you should do it.
To do it you have to be sure there is still this array.
Related
This question already has answers here:
How to find the size of an array (from a pointer pointing to the first element array)?
(17 answers)
Closed 4 years ago.
Is there any better way of getting the right length of array containing digits?
I have an array of digits: 0, 0, 1 and I try to get length of it. It obviously breaks and returns 0. I am new to C but I tried to make custom strlen function:
int custom_strlen(char *str) {
for(int i = 1; ;i++) {
if (str[i] == 0) {
return i;
}
}
return -47;
}
but it is not that efficient and in some cases returns unexpected values as well. The expected out put would be 3 in this case.
Is there any function to use?
An array of integers is not a string. C arrays do not contain length information inherently. The way strlen works is that C strings are null terminated, meaning the last character is NUL (null character), which is 0. Otherwise, there is just no way to know how long an array is.
I think you may be wanting to do an array of '0','0','1'. Can you post the array you are using?
As mentioned, C strings are null terminated.
The only choices are
Using a string terminated with some special character that you watch for (like a null) or
Keeping track of how long the string is when you create it.
FWIW, if it's not null terminated, it's not actually a string in C, it's just memory that contains chars that you happen to be interpreting as a string.
This question already has answers here:
How do I determine the size of my array in C?
(24 answers)
Closed 6 years ago.
my professor has assigned a project where we need to make a reverse polish calculator in C (input in postscript syntax). I'm currently working on a method to find the length of the array of values I have scanned in (via .txt file). My current method is
int length(int list[]) {
int c = 0;
while(0 == 0) {
if(list[c] != '\0') {c++;}
else {break;}
}
return c;
}
and the call for it is
int sizeA = length(list);
printf("\n%d\n", sizeA);
It's currently only outputting the length as 0. Does anyone know why that might be and a fix to this method?
Thanks
The notion of "length" is a sort of tricky one in C (and low-level programming in general). If you have an array, the C compiler knows how large it is and provides an interface to the programmer to get that value in bytes: sizeof. The thing is, arrays are passed via pointers in C and determining the size via pointers is impossible without certain meta-information. Common methods to determine the length of an array are
appending an end marker to the array. Determining the length is simply a matter of iterating until the end marker is found and returning the number of iterations. Note that this renders the end marker's value unavailable for use as a value in the array.
just passing the size of the array around. Take the write system call as an example. Besides the file handle, it needs a pointer to the data and its length. Why its length as well? Because the pointer doesn't contain information about the length. So, either use a terminator like a null byte or pass the length explicitly. The former idea can be abandoned because the write system call is supposed to be generic; and to yield genericity, a null byte must be expected to be a possible value in the array, so it cannot be used as a terminator for reasons I uttered above.
Which one you actually end up using totally depends on the particular use case.
Apparently you decided to use the terminator-variant. \0 is the null byte, an ASCII character with code value 0x0. It's commonly used for terminating C-strings. strlen uses it to determine a C-string's length, for example. For int arrays, there is no such predefined terminator, so you need to come up with your own one. If you decide on \0, so be it, but I'd use the literal 0x0 instead because strictly-speaking, \0 is a character literal and that's just unfitting for ints.
To actually implement this, you'd need to append the terminating value to every int array, whose size you want to determine this way. Then write your function to get the length of such an int array just as you do, i.e., by iterating until the terminator is found.
Good day everyone. I have the following c-string initialization:
*(str) = 0; where str is declared as char str[255];. The questions are:
1) is it the best way to initialize a string this way?
2) should I expect trouble with this code on 64-bit platform?
Thanks in advance.
The instruction *(str) = 0 is completely equivalent to str[0] = 0;.
The initiatlization simply place the integer 0 (which is equivalent to '\0') in the first position, making the string empty.
1) There's no "best way", the key is being consistent so if you choose the "*(str) = 0;" path, always do the same everywhere.
2) No trouble could ever come from that statement. Whatever the architecture, the compiler will take care of everything.
What this does is change the uninitialized char array str, of which each element has some undefined value, to have its first element set to the null terminator character.
As far as most string related functions go, they will traverse this string from left to right, and immediately see the null terminator.
There are surely functions that don't care about null terminators, and are given a maximum size. In these cases, you are reading undefined values, which is Badâ„¢.
Use this instead:
char str[255] = { 0 }; // zero every element
or initialize the string to something useful on first use.
This question already has answers here:
Using the equality operator == to compare two strings for equality in C [duplicate]
(9 answers)
Closed 9 years ago.
Got a small problem with C. Restricting myself to simple C (i.e. OS instructions), and two strings seem to not be the same. Here is my code:
char inputData[256];
int rid;
rid = read(0,inputData,256);
// Strip input
char command[rid];
int i;
for (i = 0; i<=rid-2; i++) {
command[i] = inputData[i];
}
command[rid-1] = '\0';
if (command == "exit") {
write(1,"exit",sizeof("exit"));
}
Now, if a user enters "exit" into the terminal when queried and hits enter, the if for detecting "exit" never gets run. Any ideas?
Thanks,
EDIT: I am commiting to git as I go, so the current version can be found at github.com/samheather/octo-os. It's very obviously not complete code, but it demonstrates the problem.
You can't compare strings with ==. You need to use strcmp.
if (strcmp(command, "exit") == 0) {
C strings are actually character arrays. You can think of "command" as a pointer to the first character. You want to compare every character in the string, not just the location of the first characters.
You should use strcmp to compare strings in C.
if(strcmp(command, "exit") == 0) //strcmp returns 0 if strings are equal
To quote:
A zero value indicates that both strings are equal. A value greater than zero indicates
that the first character that does not match has a greater value in str1 than in str2.
a value less than zero indicates the opposite.
As it stands right now, you're comparing the address of command with the address of the string literal "exit", which pretty much can't be the same.
You want to compare the contents, with either strcmp, or (if "only OS instructions" means no standard library functions) an equivalent you write yourself that walks through the strings and compares characters they contain.
As others said, == doesn't work with strings. The reason is that it would compare the pointers given.
In the expression
command == "exit"
command is a pointer to your array variable, while "exit" is a pointer to that string which resides in read-only data space. They can never be identical, so the comparison always is false.
That's why strcmp() is the way to go.
Use strcmp from the standard library.
This might seem like a very simple question, but I am struggling with it. I have been writing iPhone apps with Objective C for a few months now, but decided to learn C Programming to give myself a better grounding.
In Objective-C if I had a UILabel called 'label1' which contained some text, and I wanted to run some instructions based on that text then it might be something like;
if (label1.text == #"Hello, World!")
{
NSLog(#"This statement is true");
}
else {
NSLog(#"Uh Oh, an error has occurred");
}
I have written a VERY simple C Program I have written which uses printf() to ask for some input then uses scanf() to accept some input from the user, so something like this;
int main()
{
char[3] decision;
Printf("Hi, welcome to the introduction program. Are you ready to answer some questions? (Answer yes or no)");
scanf("%s", &decision);
}
What I wanted to do is apply an if statement to say if the user entered yes then continue with more questions, else print out a line of text saying thanks.
After using the scanf() function I am capturing the users input and assigning it to the variable 'decision' so that should now equal yes or no. So I assumed I could do something like this;
if (decision == yes)
{
printf("Ok, let's continue with the questions");
}
else
{
printf("Ok, thank you for your time. Have a nice day.");
}
That brings up an error of "use of undeclared identifier yes". I have also tried;
if (decision == "yes")
Which brings up "result of comparison against a string literal is unspecified"
I have tried seeing if it works by counting the number of characters so have put;
if (decision > 3)
But get "Ordered comparison between pointer and integer 'Char and int'"
And I have also tried this to check the size of the variable, if it is greater than 2 characters it must be a yes;
if (sizeof (decision > 2))
I appreciate this is probably something simple or trivial I am overlooking but any help would be great, thanks.
Daniel Haviv's answer told you what you should do. I wanted to explain why the things you tried didn't work:
if (decision == yes)
There is no identifier 'yes', so this isn't legal.
if (decision == "yes")
Here, "yes" is a string literal which evaluates to a pointer to its first character. This compares 'decision' to a pointer for equivalence. If it were legal, it would be true if they both pointed to the same place, which is not what you want. In fact, if you do this:
if ("yes" == "yes")
The behavior is undefined. They will both point to the same place if the implementation collapses identical string literals to the same memory location, which it may or may not do. So that's definitely not what you want.
if (sizeof (decision > 2))
I assume you meant:
if( sizeof(decision) > 2 )
The 'sizeof' operator evaluates at compile time, not run time. And it's independent of what's stored. The sizeof decision is 3 because you defined it to hold three characters. So this doesn't test anything useful.
As mentioned in the other answer, C has the 'strcmp' operator to compare two strings. You could also write your own code to compare them character by character if you wanted to. C++ has much better ways to do this, including string classes.
Here's an example of how you might do that:
int StringCompare(const char *s1, const char *s2)
{ // returns 0 if the strings are equivalent, 1 if they're not
while( (*s1!=0) && (*s2!=0) )
{ // loop until either string runs out
if(*s1!=*s2) return 1; // check if they match
s1++; // skip to next character
s2++;
}
if( (*s1==0) && (*s2==0) ) // did both strings run out at the same length?
return 0;
return 1; // one is longer than the other
}
You should use strcmp:
if(strcmp(decision, "yes") == 0)
{
/* ... */
}
You should be especially careful with null-terminated string in C programming. It is not object. It is a pointer to a memory address. So you can't compare content of decision directly with a constant string "yes" which is at another address. Use strcmp() instead.
And be careful that "yes" is actually "yes\0" which will take 4 bytes and the "\0" is very important to strcmp() which will be recognized as the termination during the comparison loop.
Ok a few things:
decision needs to be an array of 4 chars in order to fit the string "yes" in it. That's because in C, the end of a string is indicated by the NUL char ('\0'). So your char array will look like: { 'y', 'e', 's', '\0' }.
Strings are compared using functions such as strcmp, which compare the contents of the string (char array), and not the location/pointer. A return value of 0 indicates that the two strings match.
With: scanf("%s", &decision);, you don't need to use the address-of operator, the label of an array is the address of the start of the array.
You use strlen to get the length of a string, which will just increment a counter until it reaches the NUL char, '\0'. You don't use sizeof to check the length of strings, it's a compile-time operation which will return the value 3 * sizeof(char) for a char[3].
scanf is unsafe to use with strings, you should alternatively use fgets(stdin...), or include a width specifier in the format string (such as "3%s") in order to prevent overflowing your buffer. Note that if you use fgets, take into account it'll store the newline char '\n' if it reads a whole line of text.
To compare you could use strcmp like this:
if(strcmp(decision, "yes") == 0) {
// decision is equal to 'yes'
}
Also you should change char decision[3] into char decision[4] so that the buffer has
room for a terminating null character.
char decision[4] = {0}; // initialize to 0
There's several issues here:
You haven't allocated enough storage for the answer:
char[3] decision;
C strings are bytes in the string followed by an ASCII NUL byte: 0x00, \0. You have only allocated enough space for ye\0 at this point. (Well, scanf(3) will give you yes\0 and place that NUL in unrelated memory. C can be cruel.) Amend that to include space for the terminating \0 and amend your scanf(3) call to prevent the buffer overflow:
char[4] decision;
/* ... */
scanf("%3s", decision);
(I've left off the &, because simply giving the name of the array is the same as giving the address of its first element. It doesn't matter, but I believe this is more idiomatic.)
C strings cannot be compared with ==. Use strcmp(3) or strncmp(3) or strcasecmp(3) or strncasecmp(3) to compare your strings:
if(strcasecmp(decision, "yes") == 0) {
/* yes */
}
C has lots of lib functions to handle this but it pays to know what you are declaring.
Declaring
char[3] decision;
is actually declaring a char array of length 3. So therefor attempting a comparison of
if(decision == "yes")
is comparing a literal against and array and therefor will not work. Since there is no defined string type in C you have to use pointers, but not directly, if you don't want to. In C strings are in fact arrays of char so you can declare them both ways eg:
char[3] decision ;
* char decision ;
Both will in point of fact work but you in the first instance the compiler will allocate the memory for you, but it will ONLY allocate 3 bytes. Now since strings in C are null terminated you need to actually allocate 4 bytes since you need room for "yes" and the null. Declaring it the second way simply declares a pointer to someplace in memory but you have no idea really where. You would then have to allocate memory to contain whatever you are going to put there since to do otherwise will more then likely cause a SEGFAULT.
To compare what you get from input you have two options, either use the strcomp() function or do it yourself by iterating through decision and comparing each individual byte against "Y" and "E" and "S" until you hit null aka \0.
There are variations on strcomp() to deal with uppercase and lowercase and they are part of the standard string.h library.