diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,10 @@
+2024-01-07
+        * Version bump (3.18). (#487)
+        * Change return type of main generated for tests. (#468)
+        * Print constants in tests using portable suffixes. (#471).
+        * Pass output arrays as arguments to trigger argument functions. (#431)
+        * Compliance with MISRA C 2023 / MISRA C 2012. (#472)
+
 2023-11-07
         * Version bump (3.17). (#466)
         * Replace uses of deprecated functions. (#457)
diff --git a/copilot-c99.cabal b/copilot-c99.cabal
--- a/copilot-c99.cabal
+++ b/copilot-c99.cabal
@@ -1,6 +1,6 @@
 cabal-version             : >= 1.10
 name                      : copilot-c99
-version                   : 3.17
+version                   : 3.18
 synopsis                  : A compiler for Copilot targeting C99.
 description               :
   This package is a back-end from Copilot to C.
@@ -45,9 +45,9 @@
                           , mtl                 >= 2.2 && < 2.4
                           , pretty              >= 1.1 && < 1.2
 
-                          , copilot-core        >= 3.17  && < 3.18
+                          , copilot-core        >= 3.18  && < 3.19
                           , language-c99        >= 0.2.0 && < 0.3
-                          , language-c99-simple >= 0.2.2 && < 0.3
+                          , language-c99-simple >= 0.3   && < 0.4
 
   exposed-modules         : Copilot.Compile.C99
 
diff --git a/src/Copilot/Compile/C99/CodeGen.hs b/src/Copilot/Compile/C99/CodeGen.hs
--- a/src/Copilot/Compile/C99/CodeGen.hs
+++ b/src/Copilot/Compile/C99/CodeGen.hs
@@ -100,11 +100,18 @@
 
 -- | Define an accessor functions for the ring buffer associated with a stream.
 mkAccessDecln :: Id -> Type a -> [a] -> C.FunDef
-mkAccessDecln sId ty xs = C.FunDef cTy name params [] [C.Return (Just expr)]
+mkAccessDecln sId ty xs =
+    C.FunDef static cTy name params [] [C.Return (Just expr)]
   where
+    static     = Just C.Static
     cTy        = C.decay $ transType ty
     name       = streamAccessorName sId
-    buffLength = C.LitInt $ fromIntegral $ length xs
+
+    -- We cast the buffer length to a size_t to make sure that there are no
+    -- implicit conversions. This is a requirement for compliance with MISRA C
+    -- (Rule 10.4).
+    buffLength = C.Cast sizeT $ C.LitInt $ fromIntegral $ length xs
+    sizeT      = C.TypeName $ C.TypeSpec $ C.TypedefName "size_t"
     params     = [C.Param (C.TypeSpec $ C.TypedefName "size_t") "x"]
     index      = (C.Ident (indexName sId) C..+ C.Ident "x") C..% buffLength
     expr       = C.Index (C.Ident (streamName sId)) index
@@ -113,16 +120,19 @@
 
 -- | Write a generator function for a stream.
 mkGenFun :: String -> Expr a -> Type a -> C.FunDef
-mkGenFun name expr ty = C.FunDef cTy name [] cVars [C.Return $ Just cExpr]
+mkGenFun name expr ty =
+    C.FunDef static cTy name [] cVars [C.Return $ Just cExpr]
   where
+    static         = Just C.Static
     cTy            = C.decay $ transType ty
     (cExpr, cVars) = runState (transExpr expr) mempty
 
 -- | Write a generator function for a stream that returns an array.
 mkGenFunArray :: String -> String -> Expr a -> Type a -> C.FunDef
 mkGenFunArray name nameArg expr ty@(Array _) =
-    C.FunDef funType name [ outputParam ] varDecls stmts
+    C.FunDef static funType name [ outputParam ] varDecls stmts
   where
+    static  = Just C.Static
     funType = C.TypeSpec C.Void
 
     -- The output value is an array
@@ -145,7 +155,7 @@
 -- | Define the step function that updates all streams.
 mkStep :: CSettings -> [Stream] -> [Trigger] -> [External] -> C.FunDef
 mkStep cSettings streams triggers exts =
-    C.FunDef void (cSettingsStepFunctionName cSettings) [] declns stmts
+    C.FunDef Nothing void (cSettingsStepFunctionName cSettings) [] declns stmts
   where
     void = C.TypeSpec C.Void
 
@@ -163,6 +173,16 @@
     (triggerDeclns, triggerStmts) =
       unzip $ map mkTriggerCheck triggers
 
+    -- Update the value of a variable with the result of calling a function that
+    -- generates the next value in a stream expression. If the type of the
+    -- variable is an array, then we cannot perform a direct C assignment, so
+    -- we instead pass the variable as an output array to the function.
+    updateVar :: C.Ident -> C.Ident -> Type a -> C.Expr
+    updateVar varName genName (Array _) =
+      C.Funcall (C.Ident genName) [C.Ident varName]
+    updateVar varName genName _ =
+      C.AssignOp C.Assign (C.Ident varName) (C.Funcall (C.Ident genName) [])
+
     -- Write code to update global stream buffers and index.
     mkUpdateGlobals :: Stream -> (C.Decln, C.Stmt, C.Stmt, C.Stmt)
     mkUpdateGlobals (Stream sId buff _expr ty) =
@@ -170,10 +190,7 @@
         where
           tmpDecln = C.VarDecln Nothing cTy tmpVar Nothing
 
-          tmpAssign = case ty of
-            Array _ -> C.Expr $ C.Funcall (C.Ident $ generatorName sId)
-                                          [ C.Ident tmpVar ]
-            _       -> C.Expr $ C.Ident tmpVar C..= val
+          tmpAssign = C.Expr $ updateVar tmpVar (generatorName sId) ty
 
           bufferUpdate = case ty of
             Array _ -> C.Expr $ memcpy dest (C.Ident tmpVar) size
@@ -187,13 +204,16 @@
 
           indexUpdate = C.Expr $ indexVar C..= (incIndex C..% buffLength)
             where
-              buffLength = C.LitInt $ fromIntegral $ length buff
-              incIndex   = indexVar C..+ C.LitInt 1
+              -- We cast the buffer length and the literal one to a size_t to
+              -- make sure that there are no implicit conversions. This is a
+              -- requirement for compliance with MISRA C (Rule 10.4).
+              buffLength = C.Cast sizeT $ C.LitInt $ fromIntegral $ length buff
+              incIndex   = indexVar C..+ C.Cast sizeT (C.LitInt 1)
+              sizeT      = C.TypeName $ C.TypeSpec $ C.TypedefName "size_t"
 
           tmpVar   = streamName sId ++ "_tmp"
           buffVar  = C.Ident $ streamName sId
           indexVar = C.Ident $ indexName sId
-          val      = C.Funcall (C.Ident $ generatorName sId) []
           cTy      = transType ty
 
     -- Make code that copies an external variable to its local one.
@@ -250,22 +270,8 @@
         aTmpDeclns = zipWith declare args aTempNames
           where
             declare :: UExpr -> C.Ident -> C.Decln
-            declare arg tmpVar =
-              C.VarDecln Nothing (tempType arg) tmpVar Nothing
-
-            -- Type of the temporary variable used to store values of the type
-            -- of a given expression.
-            tempType :: UExpr -> C.Type
-            tempType (UExpr { uExprType = ty }) =
-              case ty of
-                -- If a temporary variable is being used to store an array,
-                -- declare the type of the temporary variable as a pointer, not
-                -- an array. The problem with declaring it as an array is that
-                -- the `arg` function will return a pointer, not an array, and
-                -- C doesn't make it easy to cast directly from an array to a
-                -- pointer.
-                Array ty' -> C.Ptr $ transType ty'
-                _         -> transType ty
+            declare (UExpr { uExprType = ty }) tmpVar =
+              C.VarDecln Nothing (transType ty) tmpVar Nothing
 
         triggerCheckStmt :: C.Stmt
         triggerCheckStmt = C.If guard' fireTrigger
@@ -283,13 +289,14 @@
               where
                 -- List of assignments of values of temporary variables.
                 argAssigns :: [C.Expr]
-                argAssigns = zipWith assign aTempNames args'
+                argAssigns = zipWith3 assign aTempNames aArgNames args
 
-                assign :: C.Ident -> C.Expr -> C.Expr
-                assign aTempName = C.AssignOp C.Assign (C.Ident aTempName)
+                assign :: C.Ident -> C.Ident -> UExpr -> C.Expr
+                assign aTempName aArgName (UExpr { uExprType = ty }) =
+                  updateVar aTempName aArgName ty
 
-                args'         = take (length args) (map argCall (argNames name))
-                argCall name' = C.Funcall (C.Ident name') []
+                aArgNames :: [C.Ident]
+                aArgNames = take (length args) (argNames name)
 
                 -- Build an expression to pass a temporary variable as argument
                 -- to a trigger handler.
diff --git a/src/Copilot/Compile/C99/Compile.hs b/src/Copilot/Compile/C99/Compile.hs
--- a/src/Copilot/Compile/C99/Compile.hs
+++ b/src/Copilot/Compile/C99/Compile.hs
@@ -119,9 +119,8 @@
         accessDecln (Stream sId buff _ ty) = mkAccessDecln sId ty buff
 
         streamGen :: Stream -> C.FunDef
-        streamGen (Stream sId _ expr ty@(Array _)) =
-          mkGenFunArray (generatorName sId) (generatorOutputArgName sId) expr ty
-        streamGen (Stream sId _ expr ty) = mkGenFun (generatorName sId) expr ty
+        streamGen (Stream sId _ expr ty) =
+          exprGen (generatorName sId) (generatorOutputArgName sId) expr ty
 
         triggerGen :: Trigger -> [C.FunDef]
         triggerGen (Trigger name guard args) = guardDef : argDefs
@@ -130,7 +129,19 @@
             argDefs  = zipWith argGen (argNames name) args
 
             argGen :: String -> UExpr -> C.FunDef
-            argGen argName (UExpr ty expr) = mkGenFun argName expr ty
+            argGen argName (UExpr ty expr) =
+              exprGen argName (argName ++ "_output") expr ty
+
+        -- Create a function that calculates the current value generated by an
+        -- expression `expr` of type `ty`. The generator treats arrays
+        -- specially, and the function takes an output array as a parameter.
+        -- The second identifier `outputArrName` is not used if `expr` is not an
+        -- array.
+        exprGen :: C.Ident -> C.Ident -> Expr a -> Type a -> C.FunDef
+        exprGen funName outputArrName expr ty@(Array _) =
+          mkGenFunArray funName outputArrName expr ty
+        exprGen funName _ expr ty =
+          mkGenFun funName expr ty
 
 -- | Generate the .h file from a 'Spec'.
 compileH :: CSettings -> Spec -> C.TransUnit
diff --git a/tests/Test/Copilot/Compile/C99.hs b/tests/Test/Copilot/Compile/C99.hs
--- a/tests/Test/Copilot/Compile/C99.hs
+++ b/tests/Test/Copilot/Compile/C99.hs
@@ -157,7 +157,7 @@
           , "void nop () {"
           , "}"
           , ""
-          , "void main () {"
+          , "int main () {"
           , "  step();"
           , "}"
           ]
@@ -886,28 +886,52 @@
   cshow :: s -> String
 
 instance CShow Int8 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "INT8_C(" ++ show x ++ ")"
 
 instance CShow Int16 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "INT16_C(" ++ show x ++ ")"
 
 instance CShow Int32 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "INT32_C(" ++ show x ++ ")"
 
 instance CShow Int64 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "INT64_C(" ++ show x ++ ")"
 
 instance CShow Word8 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "UINT8_C(" ++ show x ++ ")"
 
 instance CShow Word16 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "UINT16_C(" ++ show x ++ ")"
 
 instance CShow Word32 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "UINT32_C(" ++ show x ++ ")"
 
 instance CShow Word64 where
-  cshow = show
+  -- Use a macro to ensure that any necessary suffixes are added to the number.
+  -- We choose this macro instead of specifically adding a suffix for reasons
+  -- of portability.
+  cshow x = "UINT64_C(" ++ show x ++ ")"
 
 instance CShow Float where
   cshow = show
