Segmentation Fault occurs after using global variable in several function - c

I'm currently programming my Bachelor Project what consists an RFB-Client and a Shared Memory. The initialisation of the RFB-Client is done, the Shared Memory is created. My teacher told me to decouple the code and I wrote several functions and used a global variable for the shared memory.
But now a segmentation fault occurs while trying to read the content of the global variable. I debugged the code and found out: The content of the global variabel "my_shm" is always "0x00" :-/
Could you please help me?
These are the portions of code where the problem occurs:
(I know, this is a long code, but sending just parts of it would be useless...)
char *my_shm; --> //global variable
int SHM_init (int shmid, char* shm, key_t key, long int size) {
/* Create a new (System V) shared memory segment of the specified size */
shmid = shmget(key, SHM_SIZE, IPC_CREAT|0777);
/* Check if SHM creation was successful */
if (shmid < 0) {
/* DBG: Debug message to show which point of the program has been passed */
DBG_PRINT("C\n");
/* Check if creation failed because of already existing SHM */
if (EEXIST == errno) {
/* DBG: Debug message to show which point of the program has been passed */
DBG_PRINT("CC\n");
/* Delete already existing SHM with shmctl */
shmctl(shmid, IPC_RMID, NULL);
} else {
/* DBG: Debug message to show which point of the program has been passed */
DBG_PRINT("CCC\n");
}
/* Creation and initialization of SHM failed */
return -1;
}
/* Attach the SHM data pointer to the previously created SHM segment */
shm = shmat(shmid, NULL, 0);
if(shm == (char *) -1) {
/* Attaching failed */
return -1;
}
DBG_PRINT("Shared Memory Initialization successful\n");
/* Creation and initialization of shared memory was successful */
return 0;
}
void RFB_update(rfbClient* client) {
DBG_PRINT("RFB_update called\n");
int i,j;
rfbPixelFormat* pformat=&client->format;
DBG_PRINT("A\n");
/*bytesPerPix: variable which stores Bytes per Pixel*/
int bytesPerPix = pformat->bitsPerPixel/8;
DBG_PRINT("B\n");
/*row= width of frame*bytes per Pixel*/
int row=client->width*bytesPerPix;
DBG_PRINT("C\n");
char byte_to_write;
//as long as j is smaller than 128*(width*bytesPerPix)
for(j=0;j<client->height*row;j+=row) {
//as long as i is smaller than 128 * bytesPerPix
for(i=0;i<client->width*bytesPerPix;i+=bytesPerPix) {
/*frameBuff: Pointer on FrameBuffer*/
unsigned char* frameBuff = client->frameBuffer+j+i;
unsigned int v;
if(bytesPerPix==4)
v=(unsigned int*)frameBuff;
byte_to_write = ((v>>pformat->redShift)*256/(pformat->redMax+1));
SHM_write_byte(my_shm,byte_to_write);
byte_to_write = ((v>>pformat->greenShift)*256/(pformat->greenMax+1));
SHM_write_byte(my_shm,byte_to_write);
byte_to_write = ((v>>pformat->blueShift)*256/(pformat->blueMax+1));
SHM_write_byte(my_shm,byte_to_write);
}
}
DBG_PRINT("RFB_update successful, Shared Memory is filled\n");
}
int SHM_write_byte (char** shm, char byte) {
/*Check if pointer to SHM is valid */
if (shm == (char **) -1) {
/* Pointer is invalid */
return -1;
}
shm = byte;
shm++;
return 0;
}
int main (int argc, char *argv[]) {
if (SHM_init(shmid, my_shm, SHM_KEY, SHM_SIZE) != 0) {
DBG_PRINT("Shared Memory initialized\n");
/* Couldn't initialize SHM,initializing failed */
return -1;
}
/* Initialize RFB Client */
if (RFB_client_init(rfb_client, (FinishedFrameBufferUpdateProc)RFB_update) != 0) {
DBG_PRINT("Couldn't initialize client\n");
/* Couldn't initialize Client,initializing failed */
return -1;
}
--> everywhere the variable "my_shm" is used: the content is: 0x00...

This seems to be a very common problem here on stackoverflow.com today, and the problem is that you pass arguments to function by value and not by reference.
What that means is that when you pass an argument to a function, its value is copied, and the function only work on the copy locally inside the function. As you should know, modifying a copy will of course not modify the original.
C does not have pass by reference, but it can be emulated by using pointers. In your case, since you have a pointer you need to pass a pointer to the pointer using the address-of operator, like
SHM_init(shmid, &my_shm, SHM_KEY, SHM_SIZE)
// ^
// |
// Note ampersand (address-of operator) here
You of course need to modify the function to actually accept a pointer to the pointer:
int SHM_init (int shmid, char** shm, key_t key, long int size)
And of course use the dereference operator * when using the variable:
*shm = shmat(shmid, NULL, 0);

Related

Accessing a shared memory buffer in another process

I am trying to solve the producer consumer problem using mutexes and a shared buffer, but am having trouble accessing values in my shared buffer struct, specifically the char array. When I invoke the producer.c file in one terminal and print the values (the input is a txt file of the alphabet) using
printf("%c", newBuff->bytes[newBuff->rear]);
the chars do appear as normal, however when I do the same thing in consumer.c, but with
printf("%c", newBuff->bytes[newBuff->front]);
the values appear blank. The newBuff->front value is zero, so it should print the letter a. When I access other values in my struct in consumer.c like front, count, or rear they are correct. Share memory creation as well as attachment also works properly so I believe the issue is either I am not storing the char values properly in the array or I am trying to access them incorrectly. In the code below I placed the printf in the loop for producer.c and then outside the loop for consumer.c so I know for a fact a value is present before the consumer starts extracting data.
Consumer.c
typedef struct buffer{
pthread_mutex_t lock;
pthread_cond_t shout;
int front;
int rear;
int count;
int endOfFile;
char bytes[1024];
} buffer;
int main(int argc, char const *argv[]) {
int i=0;
FILE *file = fopen(argv[1], "w");
if (argc != 2){
printf("You must enter in a file name\n");
}
int shmid, swapCount=0;
char swapBytes[] = "";
char path[] = "~";
key_t key = ftok(path, 7);
buffer* newBuff;
if ((shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL)) != -1) {
newBuff = (buffer*) shmat(shmid, 0, 0);
printf("successful creation\n");
newBuff->front = 0;
newBuff->count = 0;
newBuff->endOfFile = 0;
pthread_mutexattr_t attr;
pthread_condattr_t condAttr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&newBuff->lock, &attr);
pthread_condattr_init(&condAttr);
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&newBuff->shout, &condAttr);
} //shared memory creation
else if ((shmid = shmget(key, 0, 0)) != -1){
printf("%d\n", shmid);
printf("successful attachment\n" );
newBuff = (buffer*) shmat(shmid, 0, 0);
printf("%c\n", newBuff->count);
}
else{
printf("oops\n");
exit(0);
}
pthread_mutex_lock(&newBuff->lock);
printf("%c\n", newBuff->bytes[newBuff->front]);
while (newBuff->endOfFile != 1)
{
while (newBuff->count == 0){
pthread_cond_signal(&newBuff->shout);
pthread_cond_wait(&newBuff->shout, &newBuff->lock);
}
newBuff->front = ((newBuff->front + 1)%SIZE);
newBuff->count--;
}
pthread_mutex_unlock(&newBuff->lock);
shmdt(&newBuff);
//pthread_mutexattr_destroy(&attr);
//pthread_condattr_destroy(&condAttr);*/
return 0;
}
Producer.c
typedef struct buffer{
pthread_mutex_t lock;
pthread_cond_t shout;
int front;
int rear;
int count;
int endOfFile;
char bytes[1024];
} buffer;
int main(int argc, char const *argv[]) {
FILE *file = fopen(argv[1], "r");
if (argc != 2){
printf("You must enter in a file dumbass\n");
}
int shmid;
char path[] = "~";
key_t key = ftok(path, 7);
buffer* newBuff;
printf("dfasdfasdf\n");
if ((shmid = shmget(key, SIZE, 0666 | IPC_CREAT | IPC_EXCL)) != -1) {
newBuff = (buffer*) shmat(shmid, 0, 0);
printf("successful creation\n");
newBuff->front = 0;
newBuff->count = 0;
newBuff->endOfFile=0;
pthread_mutexattr_t attr;
pthread_condattr_t condAttr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&newBuff->lock, &attr);
pthread_condattr_init(&condAttr);
pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&newBuff->shout, &condAttr);
} //shared memory creation
else if ((shmid = shmget(key, 0, 0)) != -1){
printf("successful attachment\n" );
newBuff = (buffer*) shmat(shmid, 0, 0);
}
else{
printf("oops\n");
exit(0);
}
printf("%d\n", shmid);
pthread_mutex_lock(&newBuff->lock);
while (fscanf(file, "%c", &newBuff->bytes[newBuff->rear]) != EOF) //read in file
{
printf("%c\n", newBuff->bytes[newBuff->rear]);
while (newBuff->count >= SIZE){ //buffer is full
//("%c\n", newBuff->bytes[newBuff->rear]);
pthread_cond_signal(&newBuff->shout);
pthread_cond_wait(&newBuff->shout, &newBuff->lock);
}
//printf("%c\n", newBuff->bytes[newBuff->rear]);
newBuff->rear = ((newBuff->front + 1)%SIZE);
newBuff->count++;
}
newBuff->endOfFile = 1;
pthread_cond_signal(&newBuff->shout);
pthread_mutex_unlock(&newBuff->lock);
shmdt(&newBuff);
//pthread_mutexattr_destroy(&attr);
//pthread_condattr_destroy(&condAttr);
return 0;
}
There are several difficulties with your code, some already addressed in comments:
ftok() requires the path passed to it to designate an existing file, but the path you are passing does not.
You request less shared memory than you actually need: only the size of the buffer content, not of a whole struct buffer. Because the amount of shared memory actually allocated will be rounded up to a multiple of the page size, this may end up being ok, but you should ensure that it will be ok by requesting the amount you actually need.
System V shared memory segments have kernel persistence, so once created, they will continue to exist until they are explicitly removed or the system is rebooted. You never remove yours. You also initialize its contents only when you first create it. Unless you manually delete it between runs, therefore, you'll use old data -- with the end-of-file indicator set, for instance -- on the second and subsequent runs. I suggest having the consumer schedule it for removal.
The consumer prints only one byte of data from the buffer, and it does so before verifying that there is anything to read.
After adding a byte to the buffer, the producer does not update the available byte count until after signaling the consumer. At best, this is wasteful, because the consumer will not see the change in count until the next time (if any) it wakes.
The producer updates the rear index of the buffer incorrectly, based on the current front value instead of on the current rear value. The data will therefore not be written into the correct places in the buffer array.
Once the producer sets the endOfFile flag, the consumer ignores all but one of any remaining unread bytes.
If the producer leaves the count zero when it finishes, the consumer will deadlock.
I find that modified versions of your programs addressing all of these issues successfully and accurately communicate data through shared memory.
Update:
Also,
The way in which consumer and / or producer initializes the mutex and condition variable is not itself safe. It is possible for whichever process attempts the shmget() second (or third, or ...) to access those objects before the first finishes initializing them. More generally, once a shared memory segment is attached, there is no inherent memory barrier involved in writing to it. To address these issues, the natural companion to SysV shared memory is SysV semaphores.

Can not increase Size of Shared Memory

Could you please help me? I can not increase the size of my Sherd Memory. The code is written in C on Linux.
I need 65536 bytes, but just 49152 seem to be allowed... If I increase it, shmget fails...(in my code: shmid < 0)
I tried to find out my maximum shared memory size and increased it with:
sysctl -w kernel.shmmax=2147483648
But that doesn't help, the initialization again fails.
This is my code:
#define SHM_KEY 9877
#define SHM_SIZE 65536
int SHM_init (int shmid, char** shm, key_t key, long int size) {
/* Create a new (System V) shared memory segment of the specified size */
shmid = shmget(key, SHM_SIZE, IPC_CREAT|0777);
/* Check if SHM creation was successful */
if (shmid < 0) {
/* DBG: Debug message to show which point of the program has been passed */
DBG_PRINT("C\n");
/* Check if creation failed because of already existing SHM */
if (EEXIST == errno) {
/* DBG: Debug message to show which point of the program has been passed */
DBG_PRINT("CC\n");
/* Delete already existing SHM with shmctl */
shmctl(shmid, IPC_RMID, NULL);
} else {
/* DBG: Debug message to show which point of the program has been passed */
DBG_PRINT("CCC\n");
}
/* Creation and initialization of SHM failed */
return -1;
}
/* Attach the SHM data pointer to the previously created SHM segment */
*shm = shmat(shmid, NULL, 0);
if(*shm == (char *) -1) {
/* Attaching failed */
return -1;
}
DBG_PRINT("Shared Memory Initialization successful\n");
/* Creation and initialization of shared memory was successful */
return 0;
}
Thank you so much in advance...
This topic might help.
What does ipcs -l return if you increase the shmmax with sysctl -w kernel.shmmax=2147483648 ?

Sharing an array of structs using mmap

I am trying to create an array of structs that is shared between a parent and child processes. I am getting a segmentation fault when trying to access the array data.
I feel certain that the problem has something to do with the way I'm using pointers, as this is an area I'm not very comfortable with.
Please note that I edited out most of the code that didn't seem relevant.
/* structure of Registration Table */
struct registrationTable{
int port;
char name[MAXNAME];
int req_no;
};
main() {
/* the registrationTable is to be a shared memory space with each child
process able to access and update. No concurrency controls are
implemented. The parent process is responsible for cleaning up after
the kiddies.
*/
struct registrationTable base_table[REG_TABLE_SIZE];
for (int i = 0; i < REG_TABLE_SIZE; i++) {
base_table[i].req_no = 0;
memset(base_table[i].name, '\0', MAXNAME);
base_table[i].port = 0;
}
printf("\nMapping Shared Memory\n");
//set up shared memory space
//void *mmap(void *addr, size_t length, int prot, int flags,
// int fd, off_t offset);
// addr = NONE, prot = PROT_NONE, flags = MAP_SHARED
struct registrationTable *table = mmap(base_table, sizeof(base_table),
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS,
-1, 0);
while(1){
pid_t child = fork();
if (child == 0) {//is child
for(int i = 0; i < REG_TABLE_SIZE; i++) {
printf("\nExamining table looking for client at %s port: %d\n",
packet_reg.data, clientAddr.sin_port);
printf("\ntable[1].req_no: %d", ta[i].req_no);
//segmentation fault on next line
if (strcmp(table[i].name, packet_reg.data) == 0
&& table[i].port == clientAddr.sin_port) {
table[i].req_no++;
}
You haven't initialized content of the table after it was allocated by mmap. So it contains garbage. And so strcmp(table[i].name, packet_reg.data) has a great chance to go over allocated buffers and access e.g. non-allocated memory.
initialize table properly;
use strncmp for the comparison there.

Unable to access the shared memory contents

I 'm working on a sample program on mmap and shared memory. Here is the piece of code I was trying,
Process B
#include<stdio.h>
#include<sys/mman.h>
#include<fcntl.h>
#include<unistd.h>
#include<malloc.h>
typedef struct sh_mem_t{
int offset;
char *buffer;
}sh_mem;
int main(){
int fd;
sh_mem *shm_obj;
fd = shm_open("/myshm",O_RDWR,0777);
if(fd == -1){
perror("fd:ERROR");
return -1;
}
shm_obj = mmap(0,sizeof(sh_mem),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
if(shm_obj == MAP_FAILED){
perror("shm_obj:ERROR");
return -1;
}
printf("\n offset : %d \n",shm_obj->offset);
// printf("\n Good work! : %s \n",shm_obj->buffer);
return 0;
}
Process A
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<malloc.h>
#include<string.h>
#include<stdlib.h>
#include<sys/mman.h>
#include<sys/sem.h>
typedef struct sh_mem_t{
int offset;
char *buffer;
}sh_mem;
int main(int argc,char *argv[]){
int file_size = 0;
int fd_sh = 0;
sh_mem *shmptr = NULL;
int fd = 0;
char offset[2];
int no_bytes_read = 0;
int read_size = 10;
int count = 0;
int ret_val = 0;
/* Variables for semaphore */
int ret = 0;
int semid = 0;
key_t sem_key = 0;
struct sembuf op[1];
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
union semun arg;
/* Validate the i/p parameters */
if(argc < 3){
perror("argc:Did u forget the I/P file and the count 0?");
return -1;
}
printf("File : %s",argv[1]);
count = atoi(argv[2]);
/* Create a semaphore */
semid = semget(sem_key,1,IPC_CREAT | 0777);
if(semid == -1){
perror("semid:");
return -1;
}
arg.val = 1;
ret = semctl(semid,0,SETVAL,arg);
/* Open the file to read the contents */
fd = open(argv[1],O_RDONLY);
/* Calculate the total size of the file */
file_size = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
printf("\n File Size is : %d \n",file_size);
/* Create a new memory object */
fd_sh = shm_open("/myshm",O_RDWR | O_CREAT,0777);
/* Set the memory object's size */
if((ftruncate(fd_sh,sizeof(sh_mem))) == -1){
perror("ftruncate:ERROR");
return -1;
}
/* Map the Memory object */
shmptr = mmap(0,sizeof(sh_mem),PROT_READ | PROT_WRITE,MAP_SHARED,fd_sh,0);
/* Allocate the memory for the buffer */
shmptr->buffer = malloc((sizeof(char)*file_size));
printf("\nThe Map address is : 0x%08x\n",shmptr);
/* Copy the contents to the shared memory */
read(fd,&offset,1);
if(count == 0){
shmptr->offset = 0;
}
while(shmptr->offset < file_size){
/* Semaphore section Start */
op[0].sem_num=0;
op[0].sem_op=-1;
op[0].sem_flg=0;
semop(semid,op,1);
printf("\n ProcessA Entering! \n");
printf("\n initial offset value : %d \n",shmptr->offset);
if(shmptr->offset > 0){
shmptr->buffer = shmptr->buffer + shmptr->offset;
ret_val = lseek(fd,shmptr->offset,SEEK_SET);
}
no_bytes_read = read(fd,shmptr->buffer,read_size);
shmptr->offset = (read_size + shmptr->offset);
printf("\n offset : %d \n",shmptr->offset);
printf("\n contents : %s \n",shmptr->buffer);
sleep(10);
op[0].sem_op = 1;
semop(semid,op,1);
printf("\n ProcessA Leaving ! \n");
/* Semapore section End*/
}
/* Detach from the shared memory */
shmdt(shmptr);
close(fd);
close(fd_sh);
return 0;
}
I have process A, which has put the data into the shared memory containing the structure values offset and buffer. Process B wants to access the contents stored in the shared memory(offset,buffer), but I could able to access only offset. When tried to access the buffer i'm getting a segmentation fault. Why am i getting a seg fault. As the shared object is mapped to the shared memory.
Process A will put 10 bytes into the shared memory and will go to sleep, then again it continues to put the next 10 bytes and so on.
When tried to access the buffer i'm getting a segmentation fault.
buffer is declared as pointer as part of your mapped memory:
typedef struct sh_mem_t{
int offset;
char *buffer;
}sh_mem;
Transferring pointers between processes does not make sense, since the pointer does not have any meaning in the slave process - the data it points to still resides in the master process.
You need to include the actual data you want to transfer from master to slave process:
typedef struct sh_mem_t{
int offset;
char buffer[BUFSIZE];
}sh_mem;
With the updated code in the question, the following changes are necessary to make it work:
In both A and B, change the declaration of the shared memory struct to something like
typedef struct sh_mem_t{
int offset;
char buffer[1024];
}sh_mem;
In A, remove the malloc() for shmptr->buffer. Also remove the line where you adjust the buffer by adding the offset (shmptr->buffer = shmptr->buffer + shmptr->offset;) - that needs to be handled differently, if you still need it
In B, uncomment the line which prints the Good work! output.
With these changes, I was able to start the A process like ./A data.txt 0. When I then start the B process, it prints both offset and buffer content, as it was last printed by the A process.
Some additional remarks
You should use a header file to declare the sh_mem struct, and include this file in both your .c files, to make sure the declaration is consistent between A and B.
With the solution I posted above, the application will crash with file sizes > 1024. You need to handle this accordingly, to make sure to not exceed the buffer size.
Why isnt it working with pointers
You can not access (non-shared) memory from the master process in the slave process, especially not by simply passing a pointer through the shared memory (this would make the shared memory concept obsolete). The memory which you allocated with malloc() in your master process is not part of the shared memory segment, so it is not accessible from the slave process.
In addition, mmap(), by default, is not guaranteed to return the same virtual address in both processes. So, even when you pass a pointer which points to a location inside the shared memory segment in the master, it does not point anywhere useful inside the slave process, unless you pass specific parameters to mmap(). See mmap(2) for more details.

Need to convert global variables into local variables in C

Basically, I sloppily coded an OpenCL program for an assignment using these global variables:
int devType = CL_DEVICE_TYPE_GPU;
cl_int err; /* Error code returned from api calls. */
size_t global; /* Global domain size for our calculation. */
size_t local; /* Local domain size for our calculation. */
cl_platform_id cpPlatform; /* openCL platform. */
cl_device_id device_id; /* Compute device id. */
cl_context context; /* Compute context. */
cl_command_queue commands; /* Compute command queue. */
cl_program program; /* Compute program. */
cl_kernel kernel; /* Compute kernel. */
/* Create data for the run. */
float *data = NULL; /* Original data set given to device. */
float *results = NULL; /* Results returned from device. */
unsigned int correct; /* Number of correct results returned. */
cl_mem input; /* Device memory used for the input array. */
cl_mem output; /* Device memory used for the output SUM. */
int rc = EXIT_FAILURE;
Now I'm trying to make them all local in order to tidy the program up.
I converted a global variable N by just moving it away from the variables above into the main() function. I then updated every function header that used N to have 'int N' as a parameter, and passed N into any function calls that needed it as an argument. The program worked as expected.
So I suppose what I'm asking is, for the rest of these variables, will it be that simple? I understand the concepts of passing by reference and value and realise some functions may change variables, so I'll need to use pointer referencing/dereferencing. My concern is that my pointer theory is a little rough and I'm worried I'll run into problems. I also am unsure whether my defined functions can take all of these cl variables.
Also, is there anything wrong with using the same variable names within the functions?
EDIT:
As I feared, a problem does occur in the following functions when trying to localise device_id:
void deviceSetup(int devType) {
cl_platform_id cpPlatform; /* openCL platform. */
/* Connect to a compute device. */
if (CL_SUCCESS != clGetPlatformIDs (1, &cpPlatform, NULL))
die ("Error: Failed to find a platform!");
/* Get a device of the appropriate type. */
if (CL_SUCCESS != clGetDeviceIDs (cpPlatform, devType, 1, &device_id, NULL))
die ("Error: Failed to create a device group!");
}
/* Create a compute context. */
void createContext(cl_int err){
context = clCreateContext (0, 1, &device_id, NULL, NULL, &err);
if (!context || err != CL_SUCCESS)
die ("Error: Failed to create a compute context!");
}
/* Create a command commands. */
void createCommandQueue(cl_int err) {
commands = clCreateCommandQueue (context, device_id, 0, &err);
if (!commands || err != CL_SUCCESS)
die ("Error: Failed to create a command commands!");
}
void createAndCompile(cl_int err){
/* Create the compute program from the source buffer. */
program = clCreateProgramWithSource (context, 1,
(const char **) &KernelSource,
NULL, &err);
if (!program || err != CL_SUCCESS)
die ("Error: Failed to create compute program!");
/* Build the program executable. */
err = clBuildProgram (program, 0, NULL, NULL, NULL, NULL);
if (err != CL_SUCCESS)
{
size_t len;
char buffer[2048];
clGetProgramBuildInfo (program, device_id, CL_PROGRAM_BUILD_LOG,
sizeof (buffer), buffer, &len);
die ("Error: Failed to build program executable!\n%s", buffer);
}
}
You've answered your own question really. Yes, that really is all there is to it. You may want to consider combining a large number of related variables into a struct and pass just a pointer to that struct if you find you've generated massive parameter lists for your functions but that's about it. (There is a tiny degree of performance consideration relating to the number of parameters you pass to any function, but I think for now that's an unnecessary level of complication you could do without!)
There's no getting away from understanding pointers in C though (the only way to pass by reference) so a small project like this might well be an ideal time to strengthen that knowledge!
OK, let's have an example, life's always better explained that way.
We have:
int cheddar;
int montereyjack;
int brie;
void print_cheeses(void)
{
printf("I have %d cheddar %d montereyjack and %d brie\n", cheddar, montereyjack, brie);
}
void add_cheeses(void)
{
cheddar = cheddar + 1;
montereyjack = montereyjack + 1;
brie = brie + 1;
print_cheeses();
}
int main(int argc, char *argv[])
{
add_cheeses();
printf ("Now I have %d cheddars %d jacks %d bries\n", cheddar, montereyjack, brie);
}
What we need to get to is:
// By value here because we're not changing anything
void print_cheeses(int cheds, int jacks, int bries)
{
printf("I have %d cheddar %d montereyjack and %d brie\n", cheds, jacks, bries);
}
// Pointers here because we need to change the values in main
void add_cheeses(int *cheese_one, int *cheese_two, int *cheese_three)
{
*cheese_one = *cheese_one + 1; // We're following the pointer to get to the data we want to change
*cheese_two = *cheese_two + 1;
*cheese_three = *cheese_three + 1;
print_cheeses(*cheese_one, *cheese_two, *cheese_three); // We're following the pointer to get to the data we want to print
}
int main(int argc, char *argv[])
{
int cheddar = 0;
int montereyjack = 0;
int brie = 0;
add_cheeses(&cheddar, &montereyjack, &brie);
printf ("Now I have %d cheddars %d jacks %d bries\n", cheddar, montereyjack, brie);
}
But it can be a pain passing all three values each time, and since they're related you could bundle them together in one struct and just pass a pointer to that struct about.

Resources