Pointer difference and size_t - c

I want to allocate memory for holding a field extracted from a given string. The size of the field is determined by the difference of two pointers, see the following minimal example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(int argc, char *argv[])
{
const char line[] = "foo,bar,baz";
char *field_start = line;
char *field_end;
char *field;
field_end = strchr(line, ',');
field = malloc(field_end - field_start + 1);
memcpy(field, field_start, field_end - field_start);
*(field + (field_end - field_start)) = '\0';
printf("field=\"%s\"\n", field);
/* ... */
return (0);
}
Compiling this code with clang -Weverything -o ex ex.c results in the following warnings:
ex.c:14:41: warning: implicit conversion changes signedness: 'long' to 'unsigned long'
[-Wsign-conversion]
field = malloc(field_end - field_start + 1);
~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~^~~
ex.c:15:39: warning: implicit conversion changes signedness: 'long' to 'unsigned long'
[-Wsign-conversion]
memcpy(field, field_start, field_end - field_start);
~~~~~~ ~~~~~~~~~~^~~~~~~~~~~~~
As I understand it, the result of the pointer difference is of ptrdiff_t type while the malloc/memcpy expect an argument of type size_t.
So my question is how to address this and to eliminate the warning? As
field_end >= field_start the difference cannot become negative, so could the
above be safely casted to size_t
field = malloc(size_t(field_end - field_start + 1));
memcpy(field, size_t(field_start, field_end - field_start));
or are the any problems I'm overlooking?
Note:
There are no checks for return values in the above just for simplicity. field_start and _end should be const of course.

field_end >= field_start only holds in case strchr does not return NULL, i.e. nothing in the type system tells the compiler that this indeed always holds. Hence the warning is warranted. However, if you make sure that this is not the case, then (size_t)(field_end - field_start) should be fine. In order to not duplicate this all over, I'd add
size_t field_len;
/* memchr & null-check go here */
field_len = (size_t)(field_end - field_start);
...and then use field_len all over.
That being said, you may want to replace your malloc/memcpy combination with a call to strndup.

Related

assignment to 'char' from 'const char *' makes integer from pointer without a cast

I am very new to C and am encountering an issue while trying to store my next_frame in a variable. Any help would be great as I think this is probably something simple I'm just missing.
If I just change the following it works fine, only when I try to store the next_frame in a variable does it not compile.
// Doesn't compile
oled_write_raw_P(next_frame, FRAME_SIZE);
// Compiles
oled_write_raw_P(frames[abs((FRAME_COUNT - 1) - current_frame)];, FRAME_SIZE);
Full Code
#define FRAME_COUNT 5 // Animation Frames
#define FRAME_SIZE 256
#define FRAME_DURATION 200 // MS duration of each frame
// Variables
uint32_t timer = 0;
uint8_t current_frame = 0;
char next_frame;
static void render_animation(void) {
static const char PROGMEM frames[FRAME_COUNT][FRAME_SIZE] = {
// Images here, removed for example
};
// If timer is more than 200ms, animate
if (timer_elapsed32(timer) > FRAME_DURATION) {
timer = timer_read32();
current_frame = (current_frame + 1) % FRAME_COUNT;
next_frame = frames[abs((FRAME_COUNT - 1) - current_frame)];
// Set cursor position
oled_set_cursor(128, 0);
// Write next frame
oled_write_raw_P(next_frame, FRAME_SIZE);
}
}
These are the errors:
error: assignment to 'char' from 'const char *' makes integer from pointer without a cast [-Werror=int-conversion]
next_frame = frames[abs((FRAME_COUNT - 1) - current_frame)];
error: passing argument 1 of 'oled_write_raw_P' makes pointer from integer without a cast [-Werror=int-conversion]
oled_write_raw_P(next_frame, FRAME_SIZE);
The line
next_frame = frames[abs((FRAME_COUNT - 1) - current_frame)]
does not make sense.
The variable next_frame, to which you are assigning a value, has the type char. However, you are assigning it the expression
frames[abs((FRAME_COUNT - 1) - current_frame)]
which decays to a pointer to the first element of the sub-array, so the expression evaluates to a value of type const char *.
I'm not exactly sure what you want to accomplish, but I guess the solution to your problem is to change the type of next_frame to const char *, so that the types match. In order to do this, you can change the line
char next_frame;
to:
const char *next_frame;

C function to return greatest ascii value

I'm trying to create a C function to return a pointer to the char with the greatest ascii value in a string, but my function is returning 'H' instead of 'o'. I think it's something to do with the if statement in the for loop but I'm not sure what the problem is. Any help is appreciated. Thanks!
char * select_max(char str[]);
int main(void) {
printf("%c\n",select_max("Hello"));
printf("All tests passed successfully.\n");
}
char *select_max(char str[]){
int length = strlen(str);
if(length<1){//returns 0 if string length is less than one
printf("Invalid string.\n");
return 0;
}
char *max = str;
for(int i=0;i<length;i++){
if(str[i] > max){
max = str[i];
}
}
return *max;
}
Try adding a printout so that you can see your bug in action. For example, just before your if:
printf("%c > %c = %d\n", str[i], max, (str[i] > max));
I believe this will quickly reveal the bug. [Hint: there are 5 bugs in your program.]
Compiler is nice enough to give you quite useful warnings, you should follow them, read carefully and try to understand. It won't point out your logical errors but you can deduce these too in this case.
main.c: In function 'main':
main.c:4:5: warning: implicit declaration of function 'printf' [-Wimplicit-function-declaration]
printf("%c\n",select_max("Hello"));
^~~~~~
main.c:4:5: warning: incompatible implicit declaration of built-in function 'printf'
main.c:4:5: note: include '<stdio.h>' or provide a declaration of 'printf'
main.c: In function 'select_max':
main.c:10:18: warning: implicit declaration of function 'strlen' [-Wimplicit-function-declaration]
int length = strlen(str);
^~~~~~
main.c:10:18: warning: incompatible implicit declaration of built-in function 'strlen'
main.c:10:18: note: include '<string.h>' or provide a declaration of 'strlen'
You first need to include the libraries that you use functions from, printf is from stdio.h and strlen is from string.h.
main.c:22:12: warning: returning 'char' from a function with return type 'char *' makes pointer from integer without a cast [-Wint-conversion]
return *max;
^~~~
You appear to be confusing chars with char*s. Since select_max is logically supposed to return just a character, (and you are already returning a char) declaring it as below will suffice.
#include<stdio.h>
#include<string.h>
char select_max( char str[] );
main.c:19:17: warning: assignment to 'char *' from 'char' makes pointer from integer without a cast [-Wint-conversion]
max = str[i];
And in the implementation of select_max, there is a similar problem. The temporary variable to hold the highest character is of type char* where it just needs to be a char:
char select_max(char str[]){
// ^___
int length = strlen(str);
if(length<1){//returns 0 if string length is less than one
printf("Invalid string.\n");
return 0;
}
// Initialize it to 0, has to be lower than any ASCII letters in order your algorithm to work. DO NOT leave it uninitialized.
char max = 0; // <---
for(int i=0;i<length;i++){
if(str[i] > max){
max = str[i];
}
}
return max;
}
The code above defines max as a char and initializes it to 0. Assigning str to it was pointless anyway. You are iterating through your string one character at a time and storing the character in a temporary container.
Note that the code in the for loop had already written as if it was of type char.
The same algorithm of course can be implemented in various ways but I suppose this was what you were trying to do.

Using difference of pointers with printf("%.*s")

The problem I'm facing has to do with intptr_t data type and the way fprintf() takes arguments for the %.*s format. The %.*s format expect field precision to have type int, and maybe that's not unreasonable per se.
Not in this case though:
#include <stdio.h>
#include <stdint.h>
int main() {
char fname[] = "Some_File.txt";
FILE *write = fopen(fname, "w");
if (write != NULL) {
printf("\n\tType below :\n\n");
char in[501] = ""; char *p;
while (1) {
fgets(in, MAX_LN, stdin);
/*** Region with compiler warnings begins ***/
if ((p = strstr(in, "/end/")) != 0) {
intptr_t o = p - in;
fprintf(write, "%.*s", o, in);
/*** Region with compiler warnings ends ***/
fclose(write);
break;
} else {
fputs(in, write);
}
}
}
}
If I compile this, it doesn't play well with %.*s, and the compiler points that out:
warning: field precision should have type 'int', but argument has type 'intptr_t' (aka 'long') [-Wformat]
If I make it int o;, it plays well with %.*s but of course isn't ideal, and the compiler says as much:
warning: implicit conversion loses integer precision: 'long' to 'int' [-Wshorten-64-to-32]
Now, this is demo code, and the max size that o can hold is 500 here, however, in my actual code, it can be 10,000 or even 100,000 (still very much within the size of a 32-bit int, isn't it?)
So what will resolve this best with the least changes?
Compiled on Clang (might be very similar on GCC) with -Wall -Wextra -pedantic.
The difference of two pointers is type ptrdiff_t. "... is the signed integer type of the result of subtracting two pointers;"
// intptr_t o = p-in;
ptrdiff_t o = p - in;
Given these both point in char in[501], the difference also fits in an int.
Simply cast. The .* expects a match int, not a intptr_t nor ptrdiff_t.
// fprintf(write,"%.*s",o,in);
fprintf(write,"%.*s", (int) o, in);
Or all at once:
fprintf(write,"%.*s", (int) (p - in), in);
The type of a pointer difference such as p-in is ptrdiff_t, not intptr_t. Anyway, one alternative in this case would be to use fwrite:
if ((p = strstr(in, "/end/")) != NULL) {
size_t len = (size_t)(p - in); // p - in is an offset into data with a size
fwrite(in, sizeof(char), len, write);
fclose(write);
break;
} else {
fputs(in, write);
}
Then add error checks.

How to make strcmp() work in my C program

I am about to make an encryption of a textstring, using another textstring as a "key" for my encryption. It is basically just a reorganization of the ASCII-characters.
The key is given and is structured in a bad way, requiring some extra programming. The ASCII-characters are listed in this key with one charachter for each line, and the corresponding encrypted characters two indexes away.
This is the example of "key.txt":
A g
B 9
C ü
D (
E z
...continuing for all ASCII-characters. An encryption in my program would therfore result in:
"EDABEDA" -> "z(g9z(g"
When I am doing the encryption.
I let the program take in an input-string and I create another string with the encryption-key-characters.
I go through each character in the input string with a for-sling. For each character in the input string, I check if there is a matching character in my encryption-key string. In the encryption key I jump 4 steps at a time since I am going to encrypt and only have to compare A,B,C,D...
I use strcmp() to find a match. And when there is a match, when the character in the input string is the same as in the encryption key, I write the encrypted character to the output string. The encrypted character is placed two indexes ahead of the main character in the key-string.
The warnings occur in the strcmp()
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 3000
void encryption(char *input);
int main()
{
char input[] = "EDABEDA";
encryption(input);
}
void encryption(char *input)
{
int length,i,k;
size_t result;
char dicc[1000];
char output[SIZE];
FILE *f;
f = fopen("key.txt","r");
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
result = fread(dicc, 1, length, f);
int lenDic = strlen(dicc);
int lenInp = strlen(input);
for (i = 0 ; i < lenInp ; i++)
{
for (k = 4 ; k < lenDic ; k = k + 4)
{
if (strcmp(input[i],dicc[k]) == 0)
{
output[i] = dicc[k+2];
printf("%c",output[i]);
}
}
}
fclose(f);
}
I get the below warnings, and the program doesn't work. Anyone who can help me with this strcmp-warning and know how I should rearrange my program in order to meet its requirements?
warning: passing argument 1 of ‘strcmp’ makes pointer from integer without a cast [enabled by default]
if (strcmp(input[i],dicc[k]) == 0)
^
In file included from crypt.c:3:0:
/usr/include/string.h:144:12: note: expected ‘const char *’ but argument is of type ‘char’
extern int strcmp (const char *__s1, const char *__s2)
^
warning: passing argument 2 of ‘strcmp’ makes pointer from integer without a cast [enabled by default]
if (strcmp(input[i],dicc[k]) == 0)
^
In file included from crypt.c:3:0:
/usr/include/string.h:144:12: note: expected ‘const char *’ but argument is of type ‘char’
extern int strcmp (const char *__s1, const char *__s2)
^
Use strcmp() to compare strings, not characters. For your case this
if (input[i] == dicc[k])
should work.
Note that char is just an integer type not a string type, there is no string type in c. And one more thing, write safe code! Check of fopen() succeeded, and if length doesn't exceed 999. Pretty much every thing that can be considered an error.

Converting string to long using strtol and pointers

My goal is to convert a string such as "A1234" to a long with value 1234. My first step was to just convert "1234" to a long, and that works as expected:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char* test = "1234";
long val = strtol(test,NULL,10);
char output[20];
sprintf(output,"Value: %Ld",val);
printf("%s\r\n",output);
return 0;
}
Now I am having trouble with pointers and trying to ignore the A at the beginning of the string. I have tried char* test = "A1234"; long val = strtol(test[1],NULL,10); however that crashes the program.
How do I set this up properly to get it pointing to the correct spot?
You are almost right. You need to pass a pointer to strtol, though:
long val = strtol(&test[1], NULL, 10);
or
long val = strtol(test + 1, NULL, 10);
Turning on some compiler warning flags would have told you your problem. For example, from clang (even with no special flags added):
example.c:6:23: warning: incompatible integer to pointer conversion passing
'char' to parameter of type 'const char *'; take the address with &
[-Wint-conversion]
long val = strtol(test[1],NULL,10);
^~~~~~~
&
/usr/include/stdlib.h:181:26: note: passing argument to parameter here
long strtol(const char *, char **, int);
^
1 warning generated.
and from GCC:
example.c: In function ‘main’:
example.c:6: warning: passing argument 1 of ‘strtol’ makes pointer from integer
without a cast
Editorial note: I think you can see from these error messages why beginners are often well-advised to use clang rather than GCC.

Resources