C - manipulating char * across functions - c

I have the below function which when called from main, returns a formatted filename (000.jpg) as a string when given an int. (I know we can use sprintf for this)
Initialising char fn[8] = "000.jpg" in main works.
When passed into function getfilename(), assigning indiv chars e.g. fn[4] = 'p'; works,
but it won't work if I assign fn = "000.gif"; I get a Bus error: 10
What am I doing wrong?
(The rest of the code works fine, and output is correct so long as I don't do the line: fn = "000.gif";
But I want to learn how to be able to manipulate the string when passed across functions)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
char *getfilename(int counter, char *fn);
int main(void) {
int counter=14;
char fn[8]= "000.jpg"; // this is fine
getfilename(counter, fn);
printf("%s\n", fn);
}
char *getfilename(int counter, char *fn) {
fn[7] = '\0';
fn[4] = 'p'; // this is fine
fn[5] = 'n';
fn[6] = 'g';
//fn = "000.gif"; // this will return Bus error: 10
// ITOA IMPLEMENTATION FOR 3-DIGIT FILENAME
int numOfDigits = log10(counter) + 1;
for (int i=numOfDigits-1; i>=0; i--, counter/=10) {
if (numOfDigits==1) {
fn[i+2] = (counter % 10) + '0';
fn[0] = fn[1] = '0';
}
else if (numOfDigits==2) {
fn[i+1] = (counter % 10) + '0';
fn[0] = '0';
}
else {
fn[i] = (counter % 10) + '0';
}
}
//////////
return fn;
}

Change fn = "000.gif" to strcpy(fn, "000.gif")
And a word of advice. It could be a good thing to change char fn[8]="000.jpg" to char fn[]="000.jpg". What if you use a longer string in the future. Will you remember to increase the size of the array?
On the other hand, if you know for certain that the size will never change, you could make the code a bit more robust with something like this:
struct filename {
char fn[8];
};
char *getfilename(int counter, struct filename *arg) {
char *fn = arg->fn;
// Keep the rest the same
}
int main(void) {
int counter = 14;
struct filename f = { "000.jpg" };
getfilename(counter, &f); // Note the &
printf("%s\n", f.fn);
That way, you would get warnings if you're trying to pass a random string to the function. Note that . and -> is basically doing the same thing, but the latter is used with pointers to struct. Also note that - unlike arrays - you don't have to pass structs via pointers. You can choose to pass the directly, just like variables. Same goes for returning and assigning them. I chose pointers in this case for no other reason than that I have to choose one.
Another option that's more convenient, although the syntax may seem a bit scary is this:
char *getfilename(int counter, char (*arg)[8]) {
char *fn = *arg;
// Keep the rest of the function
I actually wrote a question about that method right now: How do I force a warning from using an array of wrong size when passed to function?
Both methods have their pros and cons.

In this statement
fn = "000.gif";
you assigned the local pointer fn (function parameters are function local variables) with the address of the first character of the string literal "000.gif". And then you are trying to change the string literal as for example
fn[0] = fn[1] = '0';
Any attempt to change a string literal results in undefined behavior.
From the C Standard (6.4.5 String literals)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
What you need is to change the content of the original array fn declared in main instead of changing the pointer fn declared as a parameter of the function getfilename. You can do it the following way
strcpy( fn, "000.gif" );

Related

C - Unable to copy a string in a function

This function is to split string based on \n and see if the row number is selected. If the row number matched, this string should be copied and used by other function:
void selectDeparment(char* departments, int selectedNum, char* selectedDepartment){
char* copyOfDepartments = malloc(strlen(departments)+1);
strcpy(copyOfDepartments,departments);
char* sav1 = NULL;
char* token = strtok_s(copyOfDepartments,"\n",&sav1);
int counter = 0;
while(token != NULL){
if(counter == selectedNum){
selectedDepartment = malloc(strlen(token)+1);
strcpy(selectedDepartment,token);
}
++counter;
token = strtok_s(NULL, "\n", &sav1);
}
}
This function is called in main like:
char* selectedDepartment;
selectDeparment(recordsPtr[0], 1, selectedDepartment);
printf(selectedDepartment);
recordsPtr[0] contains four strings with \n at the end:
aDeparment
anotherDepartment
newDepartment
otherDepartment
In C, we are encouraged to use pointer to get a value from function instead of returning a string from a function. However, the prinft in main function gives random output
I believe there is some confusion in the way you are using pointers here. Let me clarify.
In the main function, the character pointer selectedDepartment holds a certain memory in the computer. But when a function call is made to void selectDeparment(char* departments, int selectedNum, char* selectedDepartment), a new copy of selectedDepartment is created. Henceforth any changes which are made to selectedDepartment are done only at the scope of the called function and does not impact the original pointer in the main function.
Thus one clear way to solve this problem will be to pass a pointer to the character pointer defined in the main function. This will then give the correct/expected results.
Here is the modified version of the function -
void selectDeparment(char* departments, int selectedNum, char** selectedDepartment){
char* copyOfDepartments = malloc(strlen(departments)+1);
strcpy(copyOfDepartments,departments);
char* sav1 = NULL;
char* token = strtok_s(copyOfDepartments,"\n",&sav1);
int counter = 0;
while(token != NULL){
if(counter == selectedNum){
(*selectedDepartment) = malloc(strlen(token)+1);
strcpy(*selectedDepartment,token);
}
++counter;
token = strtok_s(NULL, "\n", &sav1);
}
}
And this is how it is called from the main function -
int main() {
char* recordsPtr[] = {"aDeparment\nanotherDepartment\nnewDepartment\notherDepartment"};
char* selectedDepartment;
selectDeparment(recordsPtr[0], 1, &selectedDepartment);
printf(selectedDepartment);
}
I think you are getting confused with the "A Pointer To What?" you are supposed to return. In your selectDeparment() function, if I understand what is needed, is you simply need to return a pointer to the correct department within recordsPTR. You do not need to allocate or tokenize to do that. You already have the index for the department. So simply change the return-type to char * and return departments[selectedNum];.
For example, you can whittle-down your example to:
#include <stdio.h>
char *selectDeparment (char **departments, int selectedNum){
return departments[selectedNum];
}
int main (void) {
char *selectedDepartment = NULL;
char *recordsPTR[] = { "aDepartment\n",
"anotherDepartment\n",
"newDepartment\n",
"otherDepartment\n" };
selectedDepartment = selectDeparment (recordsPTR, 1);
fputs (selectedDepartment, stdout);
}
Note: the '*' generally goes with the variable name and not the type. Why? Because:
int* a, b, c;
certainly does NOT declare three-pointers to int,
int *a, b, c;
makes clear that you have declared a single-pointer to int and two integers.
Example Use/Output
Running the example above you would have:
$ ./bin/selectedDept
anotherDepartment
You will want to add array bounds protection to ensure the index passed does not attempt to read past the array bounds. That is left to you.
If You Must Use void
If you must use a void type function, then you can pass the Address Of the pointer to the function so the function receives the original address for the pointer in main(). You can then assign the correct department to the original pointer address so the change is visible back in main(). When you pass the Address Of the pointer, it will require one additional level of indirection, e.g.
#include <stdio.h>
void selectDeparment (char **departments, int selectedNum, char **selectedDeparment) {
*selectedDeparment = departments[selectedNum];
}
int main (void) {
char *selectedDepartment = NULL;
char *recordsPTR[] = { "aDepartment\n",
"anotherDepartment\n",
"newDepartment\n",
"otherDepartment\n" };
selectDeparment (recordsPTR, 1, &selectedDepartment);
fputs (selectedDepartment, stdout);
}
(same result, same comment on adding array bounds protection)
Look this over and let me know if I filled in the missing pieces correctly. If not, just drop a comment and I'm happy to help further.

Passing a non-empty string to snprintf causes an unrelated char* array to change addresses

I'm working on the exercises in K&R's book, and I've run into a weird bug while trying to extend 04-06 to allow for variables with string names. Truthfully, I've actually managed to fix the bug (pretty simple - explained below), but I'd like to know why the error was occuring in the first place.
For those unfamiliar with the problem, you're basically asked to create a command-line calculator (using Polish notation) that can store and recall variables with character names.
Here's the relevant code where the issue occurs:
#define MAXOPLEN 1000
int varCount = 1;
char **keys;
char **values;
// changing the declaration to:
// char strOps[][STROPCOUNT] = { ... };
// fixed the issue
char *strOps[STROPCOUNT] = { "dupe", "swap", "del", "print",
"clr", "sin", "cos", "tan",
"exp", "pow", "ln", "log",
"mem", "re"};
main() {
keys = malloc(varCount * sizeof(char[MAXOPLEN]));
keys[0] = "ans";
values = malloc(varCount * sizeof(char[MAXOPLEN]));
values[0] = "0.0";
... // Other stuff related to the program
}
// flag is unrelated to the problem I'm asking about. It just checks to see
// if the variable name used to store value n is 'ans', which is where
// the last returned value is stored automatically
void memorize(char s[], double n, bool flag) {
... // small conditional block for flag
for (i = 0; i < varCount; i++) {
if (equals(keys[i], s)) {
found = True;
// Next line is where the program actually breaks
snprintf(values[i], MAXOPLEN, "%f", n);
break;
}
}
if (!found) {
i = varCount;
varCount++;
keys = realloc(keys, varCount * sizeof(char*));
keys[i] = malloc(sizeof(char[MAXOPLEN]));
keys[i] = s;
values = realloc(values, varCount * sizeof(char*));
values[i] = malloc(sizeof(char[MAXOPLEN]));
snprintf(values[i], MAXOPLEN, "%f", n);
}
}
After compiling and running, the first time you enter in an equation to calculate, everything seems to run smoothly. However, while debugging, I found out that the first three char* in strOps were oddly made to point to different addresses. When trying to save the return value of the equation to "ans", it enters the for-loop in memorize() that tries to see if string s had been used as a key name already. It correctly finds keys[0] to point to a string matching s's value ("ans"), then attempts to convert double n to a string and save it in values[0].
While inside the snprintf() function, the first three char* in strOps are made to point elsewhere inside this method in corecrt_stdio_config.h:
_Check_return_ _Ret_notnull_
__declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void)
{
// Error occurs after this next line:
static unsigned __int64 _OptionsStorage;
return &_OptionsStorage;
}
As commented in the code above, making strOps a 2D array of characters (rather than an array of char pointers) fixed the issue. This makes sense because arrays of characters can't have the values of individual characters changed, but what I don't understand is why the that method in corecrt_stdio_config.h was changing the values of those three pointers in the first place.
Thanks!
Your initializations are incorrect and are causing the change:
keys[0] = "ans";
values[0] = "0.0";
Both "ans" and "0.0" are string literals and cannot be used to initialize the arrays, you need to use strcpy after you allocate.
strcpy (keys, "ans");
strcpy (values, "0.0");
Your other option is to assign one character at a time:
size_t i;
char *p = "ans";
for (i = 0; i < strlen (p); i++)
keys[i] = p[i]; /* copy to keys */
p[i] = 0; /* nul-terminate */
note: these are examples of your errors, you do the same thing throughout your code.

Comparing the numeric values of ASCII strings in C

I am writing two ASCII char arrays to an LCD screen perfectly fine. However I need an if-condition to compare these two values.
Originally I attempted to simply compare them like this:
if(currentTemp < triggTemp)
{
alarmTriggered = true;
}
As this didn't work, I am attempting to convert them to floats from their ASCII strings using atof().
However this doesn't seem to want to work either, am I missing something silly here? Necessary code below:
void main () {
char triggTemp;
int buttonBool = 0;
bool alarmTriggered = false;
char currentTemp;
double f_triggTemp = 0;
double f_currentTemp = 0;
TRISC = 0x00;
init();
Init_lcd();
while(1)
{
//char bufferString[4];
currentTemp = get_temp();
f_currentTemp = atof(currentTemp);
f_triggTemp = atof(triggTemp);
if(f_currentTemp < f_triggTemp)
{
alarmTriggered = true;
}
if(alarmTriggered == true)
{
soundBuzzer();
}
}
The values are being returned from functions in this form:
//some function
char bufferString[4];
sprintf(numberString, "%s.%s", itoa(bufferString,setTemp,10),
itoa(bufferStringDec,setTempDec,10));
return numberString;
In essence, I am trying to compare these char arrays of ASCII characters so I can use an if condition to trigger an alarm.
I tried to keep the code snippets short, I can clarify on request. Thanks for any help.
EDIT: I know I'm using atof into double variables; the prototype in my library is set up like that.
I think this is what you are doing:
char* some_function() {
char temporary_string_buffer[32]; // Or some other fixed size
snprintf(temporary_string_buffer, 32, "some format");
return temporary_string_buffer;
}
You can't do that. OK, you can do that -- you probably just did -- but it has Undefined Behaviour, because the lifetime of temporary_string_buffer ends with the return statement. In the caller, the function will be returning what's colloquially known as a "dangling pointer"; in other words, a pointer whose target no longer has any meaning. So by the time you get around to calling atof on that value, it may have been used for something completely different.
If you want to return a string to the caller, either:
Dynamically allocate the string with malloc and make sure the caller knows that they need to free it; or
Get the caller to give you the address of a buffer and its length (as arguments), and fill in the supplied buffer. You can use the return code for a success indicator, or (like sprintf) as a count of bytes, or whatever.
char currentTemp; ... atof(currentTemp); will not work. double atof(const char *nptr) expects a pointer to a string, not a char.
Create a function to do the compare for you. Here is a complete test program. You just compare the array element one by one.
int is_greater(char a[], char b[], int z, int z1) {
int i = 0;
for( i = 0; i < z; i++ ) {
if( a[i] > b[i] ) return 1;
}
return 0;
}

C - Modifying function parameter

I'm trying to simulate passing by reference and apparently I'm doing something wrong:
long int toNumber(char * input) {
char* pointer;
long int number;
number = strtol(input, &pointer, 10);
if (number == 0L) {
return -1337;
} else {
input = pointer;
return number;
}
}
I'd like for this function to return the value of the input and trim the converted beginning of it, yet when I try calling it, even though the integer conversion is flawless, the string remains the same. Thanks for any kind of help.
Edit:
char* input = "123 456";
long int number = toNumber(input);
Edit 2: Changed back, sorry.
I'd like for it to work something like this:
input = 123 456 -> number = 123, input = 456 -> number = 456, input = NULL
It is not clear about the 1337 thing... however, this code should produce what the question code is attempting with simplified logic.
long int toNumber(char **input)
{
char *pointer;
long int number;
number = strtol(input, &pointer, 10);
if(number)
*input = pointer;
else
number = (1337);
return number;
}
Something like the following call the above function:
{
char *test="123 456";
long int n;
...
n = toNumber(&test);
printf("n[%ld] remainder[%s]\n", n, test);
...
}
Which should print:
n[123] remainder[ 456]
input is a pointer, which is passed by value to your toNumber function. Assigning a value to input merely changes the parameter object, which is local to the function. It has no effect on the caller.
To modify the string, you'd have to modify *input, or input[some_index], or pass input to some other function that will modify the data that it points to.
Note that updating a string in place is not necessarily a good idea. You can modify the characters that make up the string, but you can't increase the size of the array containing the string. And if the caller passes a string literal, then attempting to modify it has undefined behavior (and will likely crash your program).

Functions and returning values in C

Im trying to print out the part at the end of this program. I enter C17 and the part comes out as 0 when it should be 1. Why is this?
Kind Regards
Dennis
# include <stdio.h>
int Part;
int getPartType(int Part);
int calcPrice(int Part);
int main(int argc, char * argv[]){
getPartType(Part);
calcPrice(Part);
return 0;
}
// Part1: Asks for input from user for part type
int getPartType(int Part) {
int nvr;
char character_one;
char character_two;
int number;
printf("Enter the part type (C17, F25, DN3, GG7 or MV4): ");
nvr = scanf("%c%c%d",&character_one,&character_two,&number);
if (number==7 && character_two=='1') {
Part=1;
}else if (number==5 && character_two=='2') {
Part=2;
}else if (number==3 && character_two=='N') {
Part=3;
}else if (number==7 && character_two=='G') {
Part=4;
}else if (number==4 && character_two=='V') {
Part=5;
}else{
printf("Wrong Part Type\n");
Part=0;
}
return Part;
}
int calcPrice(int Part) {
printf("%d\n",Part);
return 0;
}
getPartType(Part); returns an int, and doesn't assign to the original Part. So you must change this line:
getPartType(Part);
to
Part = getPartType(Part);
If you want to change the original value of Part you must use pointers. You can read more about this in any decent C book (I recommend K&R). For example:
// takes pointer to integer and sets it to 5
void settofive(int *someInteger) {
*someInteger = 5; // dereference someInteger and set to 5
}
int main(int argc, char *argv[]) {
int test = 0;
int *ptrTotest = &test; // take address of test and store in ptrTotest
printf("%d\n", test); // prints out zero
settofive(ptrTotest);
printf("%d\n", test); // prints out five
return 0;
}
You have a little misunderstanding of function argument passing.
When you call a function like
getPartType(Part);
C will create a copy of Part on the stack and all computations within the function will be made on this copy. Therefore you will not change the variable Part. This is called Call-by-value.
To change this problem, there are two ways. You can either just use:
Part = getPartType(Part);
This will create a copy of Part, the function will work on this copy, and then return something. This something will then get stored in the original Part. In your case you can actually just use int getPartType(void) as the function declaration, because you don't work an Part.
The other way would be to pass a pointer:
getPartType(&Part);
This passes a pointer to the original Part, so you can manipulate the original part (using the *-operator). This would mean that your declaration shoudl like like void getPartType(int *). But I would say the first method is preferable if you are dealing with just one basic variable
C is call by value. This means that the function can't change the value of a variable in the caller's context, unless the caller passes the address of that value.
Since your function doesn't really need an input argument, it should be removed. All you need is the return value.
Also, you could consider using multiple return statements, changing the if-ladder to look like so:
if (number==7 && character_two=='1') {
return 1;
}else if (number==5 && character_two=='2') {
return 2;
and so on.
Further, the use of "magical" numerical constants is generally a bad idea. It would be better to introduce an enumeration before main(), like this:
enum Part { PART_C17 = 1, PART_F25, PART_DN3, PART_GG7, PART_MV4 };
Then change the function to return a value of this new type:
enum Part getPartType(void)
{
/* ... */
}
and update the code in the if-ladder accordingly:
if (number==7 && character_two=='1') {
return PART_C17;
}else if (number==5 && character_two=='2') {
return PART_F25;
and so on.

Resources